Javascript 匿名函数和全局变量

发布于 2024-09-02 03:55:33 字数 827 浏览 9 评论 0原文

我想我会尝试聪明地创建一个自己的等待函数(我意识到还有其他方法可以做到这一点)。所以我写道:

var interval_id;
var countdowntimer = 0;

function Wait(wait_interval) {
  countdowntimer = wait_interval;

  interval_id = setInterval(function() {
    --countdowntimer <=0 ? clearInterval(interval_id) : null;
  }, 1000);

  do {} while (countdowntimer >= 0);
}

// Wait a bit: 5 secs
Wait(5);

除了无限循环之外,这一切都有效。经检查,如果我取出 While 循环,则匿名函数将按预期输入 5 次。显然,全局变量 countdowntimer 是递减的。

但是,如果我在 While 循环中检查 countdowntimer 的值,它永远不会下降。尽管事实上匿名函数是在 While 循环中被调用的!

显然,不知何故,有两个 countdowntimer 值浮动,但为什么呢?

编辑

好的,所以我(现在)明白Javascript是单线程的。这在某种程度上回答了我的问题。但是,在这个单线程的处理过程中,所谓的使用setInterval的异步调用实际上是在哪一点发生的呢?只是在函数调用之间吗?当然不是,那么那些需要很长时间执行的函数呢?

I thought I would try and be clever and create a Wait function of my own (I realise there are other ways to do this). So I wrote:

var interval_id;
var countdowntimer = 0;

function Wait(wait_interval) {
  countdowntimer = wait_interval;

  interval_id = setInterval(function() {
    --countdowntimer <=0 ? clearInterval(interval_id) : null;
  }, 1000);

  do {} while (countdowntimer >= 0);
}

// Wait a bit: 5 secs
Wait(5);

This all works, except for the infinite looping. Upon inspection, if I take the While loop out, the anonymous function is entered 5 times, as expected. So clearly the global variable countdowntimer is decremented.

However, if I check the value of countdowntimer, in the While loop, it never goes down. This is despite the fact that the anonymous function is being called whilst in the While loop!

Clearly, somehow, there are two values of countdowntimer floating around, but why?

EDIT

Ok, so I understand (now) that Javascript is single threaded. And that - sort of - answers my question. But, at which point in the processing of this single thread, does the so called asynchronous call using setInterval actually happen? Is it just between function calls? Surely not, what about functions that take a long time to execute?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

不醒的梦 2024-09-09 03:55:33

周围没有该变量的两个副本。 Web 浏览器中的 JavaScript 是单线程(除非您使用新的 Web Worker 工具)。因此匿名函数永远没有机会运行,因为 Wait 占用了解释器。

您不能在基于浏览器的 Javascript 中使用忙等待函数;不会发生任何其他事情(并且在大多数其他环境中它们是一个坏主意,即使它们是可能的)。您必须改用回调。这是对其的简约改造:

var interval_id;
var countdowntimer = 0;

function Wait(wait_interval, callback) {
    countdowntimer = wait_interval;

    interval_id = setInterval(function() {
        if (--countdowntimer <=0) {
            clearInterval(interval_id);
            interval_id = 0;
            callback();
        }
    }, 1000);
}

// Wait a bit: 5 secs
Wait(5, function() {
    alert("Done waiting");
});

// Any code here happens immediately, it doesn't wait for the callback

编辑回答您的后续问题:

但是,在这个单线程的处理过程中,所谓的使用 setInterval 的异步调用实际上是在哪一点发生的呢?只是在函数调用之间吗?当然不是,那些需要很长时间执行的函数怎么办?

几乎是的,所以函数不要长时间运行很重要。 (从技术上讲,它甚至不在函数调用之间,因为如果您有一个函数调用其他三个函数,则解释器在该(外部)函数运行时无法执行任何其他操作。)解释器本质上维护了一个它需要的函数队列执行。它首先执行任何全局代码(而不是像一个大函数调用)。然后,当事情发生时(用户输入事件、达到通过 setTimeout 安排的调用回调的时间等),解释器会将其需要进行的调用推送到队列中。它总是处理队列前面的调用,因此事情可以堆积起来(就像您的 setInterval 调用,尽管 setInterval 是一个特殊 - 如果前一个回调仍在队列中等待处理,则它不会对后续回调进行排队)。因此,请考虑代码何时获得控制权以及何时释放控制权(例如,通过返回)。解释器只能在您释放控制权之后并将控制权再次交还给您之前执行其他操作。同样,在某些浏览器(例如 IE)上,同一线程还用于绘制 UI 等,因此 DOM 插入(例如)不会显示,直到您将控制权释放回浏览器以便它可以获取继续画它。

当 Web 浏览器中使用 Javascript 时,您确实需要采用事件驱动的方法来设计和编码解决方案。典型的例子是提示用户输入信息。在非事件驱动的世界中,您可以这样做:

// Non-functional non-event-driven pseudo-example
askTheQuestion();
answer = readTheAnswer();      // Script pauses here
doSomethingWithAnswer(answer); // This doesn't happen until we have an answer
doSomethingElse();

这在事件驱动的世界中不起作用。相反,您可以这样做:

askTheQuestion();
setCallbackForQuestionAnsweredEvent(doSomethingWithAnswer);
// If we had code here, it would happen *immediately*,
// it wouldn't wait for the answer

例如,askTheQuestion 可能会在页面上覆盖一个 div,其中包含提示用户输入各种信息的字段,并在完成后单击“确定”按钮。 setCallbackForQuestionAnswered 实际上会挂钩“OK”按钮上的 click 事件。 doSomethingWithAnswer 将从字段中收集信息,删除或隐藏 div,并对信息执行某些操作。

There aren't two copies of the variable lying around. Javascript in web browsers is single threaded (unless you use the new web workers stuff). So the anonymous function never has the chance to run, because Wait is tying up the interpreter.

You can't use a busy-wait functions in browser-based Javascript; nothing else will ever happen (and they're a bad idea in most other environments, even where they're possible). You have to use callbacks instead. Here's a minimalist reworking of that:

var interval_id;
var countdowntimer = 0;

function Wait(wait_interval, callback) {
    countdowntimer = wait_interval;

    interval_id = setInterval(function() {
        if (--countdowntimer <=0) {
            clearInterval(interval_id);
            interval_id = 0;
            callback();
        }
    }, 1000);
}

// Wait a bit: 5 secs
Wait(5, function() {
    alert("Done waiting");
});

// Any code here happens immediately, it doesn't wait for the callback

Edit Answering your follow-up:

But, at which point in the processing of this single thread, does the so called asynchronous call using setInterval actually happen? Is it just between function calls? Surely not, what about functions that take a long time to execute?

Pretty much, yeah — and so it's important that functions not be long-running. (Technically it's not even between function calls, in that if you have a function that calls three other functions, the interpreter can't do anything else while that (outer) function is running.) The interpreter essentially maintains a queue of functions it needs to execute. It starts starts by executing any global code (rather like a big function call). Then, when things happen (user input events, the time to call a callback scheduled via setTimeout is reached, etc.), the interpreter pushes the calls it needs to make onto the queue. It always processes the call at the front of the queue, and so things can stack up (like your setInterval calls, although setInterval is a bit special — it won't queue a subsequent callback if a previous one is still sitting in the queue waiting to be processed). So think in terms of when your code gets control and when it releases control (e.g., by returning). The interpreter can only do other things after you release control and before it gives it back to you again. And again, on some browsers (IE, for instance), that same thread is also used for painting the UI and such, so DOM insertions (for instance) won't show up until you release control back to the browser so it can get on with doing its painting.

When Javascript in web browsers, you really need to take an event-driven approach to designing and coding your solutions. The classic example is prompting the user for information. In a non-event-driven world, you could do this:

// Non-functional non-event-driven pseudo-example
askTheQuestion();
answer = readTheAnswer();      // Script pauses here
doSomethingWithAnswer(answer); // This doesn't happen until we have an answer
doSomethingElse();

That doesn't work in an event-driven world. Instead, you do this:

askTheQuestion();
setCallbackForQuestionAnsweredEvent(doSomethingWithAnswer);
// If we had code here, it would happen *immediately*,
// it wouldn't wait for the answer

So for instance, askTheQuestion might overlay a div on the page with fields prompting the user for various pieces of information with an "OK" button for them to click when they're done. setCallbackForQuestionAnswered would really be hooking the click event on the "OK" button. doSomethingWithAnswer would collect the information from the fields, remove or hide the div, and do something with the info.

朮生 2024-09-09 03:55:33

大多数 Javascript 实现都是单线程,因此当它执行 while 循环时,它不会让其他任何东西执行,因此 interval 永远不会在 while 运行时运行,从而形成无限循环。

有许多类似的尝试在 JavaScript 中创建睡眠/等待/暂停函数,但由于大多数实现都是单线程的,因此它根本不允许您在睡眠时执行任何其他操作(!)。

进行延迟的另一种方法是编写超时。它们可以推迟一段代码的执行,但您必须在许多函数中中断它。您始终可以内联函数,以便更容易理解(并在同一执行上下文中共享变量)。

还有一些库向 JavaScript 添加了一些语法糖,使其更具可读性。

编辑:
John Resig<​​/strong> 本人发表了一篇关于 如何使用 javascript 的优秀博客文章定时器工作。他非常详细地解释了这一点。希望有帮助。

Most Javascript implementation are single threaded, so when it is executing the while loop, it doesn't let anything else execute, so the interval never runs while the while is running, thus making an infinite loop.

There are many similar attempts to create a sleep/wait/pause function in javascript, but since most implementations are single threaded, it simply doesn't let you do anything else while sleeping(!).

The alternative way to make a delay is to write timeouts. They can postpone an execution of a chunk of code, but you have to break it in many functions. You can always inline functions so it makes it easier to follow (and to share variables within the same execution context).

There are also some libraries that adds some syntatic suggar to javascript making this more readable.

EDIT:
There's an excelent blog post by John Resig himself about How javascript timers work. He pretty much explains it in details. Hope it helps.

ζ澈沫 2024-09-09 03:55:33

实际上,它几乎可以保证间隔函数永远不会在循环执行时运行,因为 JavaScript 是单线程的。

之前没有人进行Wait(而且很多人都尝试过),这是有原因的;这是根本不可能做到的。

您将不得不将您的函数分解为多个位,并使用 setTimeout 或 setInterval 来安排它们。

//first part
...
setTimeout(function(){
    //next part
}, 5000/*ms*/);

根据您的需要,这可以(应该)作为状态机实现。

Actually, its pretty much guaranteed that the interval function will never run while the loop does as javascript is single-threaded.

There is a reason why no-one has made Wait before (and so many have tried); it simply cannot be done.

You will have to resort to braking up your function into bits and schedule these using setTimeout or setInterval.

//first part
...
setTimeout(function(){
    //next part
}, 5000/*ms*/);

Depending on your needs this could (should) be implemented as a state machine.

美胚控场 2024-09-09 03:55:33

为什么不直接更改 setInterval 上的毫秒属性,而不是使用全局倒计时变量呢?像这样的东西:

var waitId;

function Wait(waitSeconds)
{
    waitId= setInterval(function(){clearInterval(waitId);}, waitSeconds * 1000);
}

Instead of using a global countdowntimer variable, why not just change the millisecond attribute on setInterval instead? Something like:

var waitId;

function Wait(waitSeconds)
{
    waitId= setInterval(function(){clearInterval(waitId);}, waitSeconds * 1000);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文