4

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

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的所谓异步调用真的发生了吗?它只是在函数调用之间吗?当然不是,那些需要很长时间才能执行的功能呢?

4

4 回答 4

5

周围没有变量的两个副本。网络浏览器中的 Javascript 是单线程的(除非你使用新的网络工作者东西)。所以匿名函数永远没有机会运行,因为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,并对信息做一些事情。

于 2010-05-18T14:07:55.903 回答
3

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

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

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

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

编辑:John Resig本人 发表了一篇关于JavaScript 计时器如何工作的优秀博文。他几乎解释得很详细。希望能帮助到你。

于 2010-05-18T14:09:08.353 回答
2

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

Wait以前没有人做过(而且有很多人尝试过)是有原因的;它根本做不到。

您将不得不求助于将您的函数分解为位并使用 setTimeout 或 setInterval 安排这些。

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

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

于 2010-05-18T14:10:03.140 回答
0

为什么不直接更改 setInterval 上的毫秒属性,而不是使用全局 countdowntimer 变量呢?就像是:

var waitId;

function Wait(waitSeconds)
{
    waitId= setInterval(function(){clearInterval(waitId);}, waitSeconds * 1000);
}
于 2010-05-18T14:09:09.620 回答