我以为我会尝试并且聪明并创建我自己的等待功能(我意识到还有其他方法可以做到这一点)。所以我写道:
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 的所谓异步调用实际上是否会发生?它只是在函数调用之间吗?当然不是,那些需要很长时间才能执行的功能呢?
答案 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);
}