为什么以下JS函数破坏了浏览器进程?

时间:2015-05-12 08:02:28

标签: javascript

var wait = function (milliseconds) {
    var returnCondition = false;
    window.setTimeout(function () { returnCondition = true; }, milliseconds);
    while (!returnCondition) {};
};

我知道有很多帖子说明为什么不尝试在Javascript中实现wait()sleep()函数。因此,这不是为了使其可用于实现目的,而是使其用于概念证明。

尝试

console.log("Starting...");wait(3000);console.log("...Done!");

冻结我的浏览器。为什么wait()似乎永远不会结束?

编辑:感谢到目前为止的答案,我不知道while循环从不允许执行任何其他代码。

那么这会起作用吗?

var wait = function (milliseconds) {
    var returnCondition = false;
    var setMyTimeOut = true;
    while (!returnCondition) {
        if (setMyTimeOut) {
            window.setTimeout(function() { returnCondition = true; }, milliseconds);
            setMyTimeOut = false;
        }
    };
    return;
};

2 个答案:

答案 0 :(得分:5)

JavaScript在单个线程中执行。只有当执行路径退出时,才能开始另一个执行路径。因此,当您启动wait(3000)时,会发生以下情况:

  • returnCondition设置为false
  • 计划超时
  • 启动无限循环。

每个<script>标记,每个事件都被处理,每次超时(以及浏览器时的UI刷新)都会启动一个单独的执行路径。因此,3000的超时不能保证在3000ms内运行,但在发动机“空闲”的3000ms之后的任何时间都可以保证。

wait函数永远不会退出,因此您的脚本的执行路径永远不会结束,并且预定的超时时间永远不会到来。

编辑:

这意味着,一旦<script>标记开始,或者Node.js已经开始执行JavaScript文件,执行必须在其他任何事情发生之前到达底部。如果函数是由于事件或超时而启动的,那么该函数需要在其他任何事情发生之前退出。

<script>
  console.log("script top");
  function theTimeout() {
    console.log("timeout top");
    // something long
    console.log("timeout bottom");
  }
  setTimeout(theTimeout, 0);
  setTimeout(theTimeout, 0);
  console.log("script bottom");
</script>

这里有三条执行路径。第一个是<script>标签:它首先打印“脚本顶部”,安排两个超时(“立即”),然后打印“脚本底部”,然后到达<script>的结尾而翻译是空闲的。这意味着它有时间执行另一个执行路径,并且有两个超时正在等待,因此它选择其中一个并开始执行它。当它正在执行时,再也没有其他任何东西可以执行(甚至是UI更新);另一个超时,即使它也是“立即”调度,也要等到第一个超时的执行路径结束。当它发生时,第二次超时结束,它也会被执行。

答案 1 :(得分:2)

JavaScript是单线程的。当您调用setTimeout时,作为参数传入的方法将被放置到异步调用堆栈中。这意味着块中的下一行代码在setTimeout调用之后立即执行,并且作为参数传入的函数将在wait方法退出后执行。

你的while循环正在等待一个在wait函数运行时永远不会发生的情况,因为设置你的标志的函数在wait函数完成之前不会运行。

实施等待的正确方法是:

var wait = function (milliseconds, onEnd) {

    window.setTimeout(function () { onEnd(); }, milliseconds);

};

wait(1000, function(){alert('hi')});

这里传入一个回调函数,该函数将在超时后执行。

如果您有多个异步样式调用,则可以使用promises。 Promise将使您的代码易于阅读,并且可以轻松地将多个异步调用链接在一起。图书馆员有很好的承诺:JQuery内置了$ .Deferred,但是如果编写node.js代码,可以使用Q.

承诺样式实现看起来像这样:

var wait = function (milliseconds) {

    var onEnd = null;

    window.setTimeout(function () { onEnd(); }, milliseconds);

    return {

    then: function(action){
            onEnd = action;
    }

    }
};

wait(1000).then(function(){alert('hi')});

https://api.jquery.com/jquery.deferred/ https://github.com/kriskowal/q

下面的书帮助我理解了这个主题:

Async JavaScript:Trevor Burnham用更少的代码构建更具响应性的应用程序 https://pragprog.com/book/tbajs/async-javascript