Javascript匿名函数和全局变量

时间:2010-05-18 14:02:59

标签: javascript global-variables anonymous-function setinterval

我以为我会尝试并且聪明并创建我自己的等待功能(我意识到还有其他方法可以做到这一点)。所以我写道:

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 会递减。

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

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

修改

好的,所以我理解(现在)Javascript是单线程的。而那 - 有点 - 回答我的问题。但是,在处理这个单线程时,使用 setInterval 的所谓异步调用实际上是否会发生?它只是在函数调用之间吗?当然不是,那些需要很长时间才能执行的功能呢?

4 个答案:

答案 0 :(得分:5)

周围没有两个变量副本。 Web浏览器中的Javascript是单线程(除非您使用new web workers stuff)。所以匿名函数永远不会有机会运行,因为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真的会在“确定”按钮上挂起click事件。 doSomethingWithAnswer将从字段中收集信息,删除或隐藏div,并对信息执行某些操作。

答案 1 :(得分:3)

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

在javascript中有许多类似的尝试来创建睡眠/等待/暂停功能,但由于大多数实现都是单线程的,所以它只是让你在睡觉时不做任何其他事情(!)

延迟的另一种方法是写超时。他们可以推迟执行一大块代码,但你必须在许多函数中打破它。您始终可以内联函数,以便更容易遵循(并在同一执行上下文中共享变量)。

还有一些库为javascript添加了一些合成的糖,使其更具可读性。

修改 John Resig 自己关于How javascript timers work的博客文章非常精彩。他几乎详细解释了这一点。希望它有所帮助。

答案 2 :(得分:2)

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

有一个原因,为什么之前没有人做过Wait(有很多人试过);它根本无法完成。

您必须使用setTimeout或setInterval将函数制成位并调度它们。

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

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

答案 3 :(得分:0)

为什么不改变setInterval上的毫秒属性,而不是使用全局倒计时器变量?类似的东西:

var waitId;

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