【问题标题】:Asynchronous behaviour. Recursive setTimeout doesn't complete异步行为。递归 setTimeout 未完成
【发布时间】:2018-05-25 09:58:52
【问题描述】:

我不明白为什么我的程序在执行下一行代码之前没有完成递归函数。

console.clear();
var a = 1;

function logA() {
  if (a<11) {
    console.log(a);
    a++;
    setTimeout(logA, 100);
  }
  else {
    return;
  }
}

logA();
console.log("after set timeout");

示例: https://jsbin.com/moyanudeki/edit?js,console

堆栈中发生了什么?

【问题讨论】:

  • 更改 setTimeout(logA, 100);设置超时(logA(),100);你忘了 ()
  • 我只是想了解调用堆栈的行为方式。我想我应该在“设置超时后”之前看到 1...10 记录
  • @Mgasmi 不,这会导致它根本无法工作
  • @Mgasmi 除非我弄错了,否则它将执行 logA 函数,而不是将其传递给 setTimeout 以在延迟后执行。所以我会得到我需要的输出,而不是延迟。

标签: javascript asynchronous settimeout


【解决方案1】:

这里没有递归。 setTimeout,顾名思义,在设置的时间量(在本例中为 100 毫秒)到期后,调度一个异步执行的函数。这不会阻止当前批次的代码运行。

logA() 将返回,控制台打印 "after set timeout",然后 VM 等待 100 毫秒到期。发生这种情况时,它会执行logA(),它会再次安排自己,依此类推。

为了清楚起见,this 将是递归:

console.clear();
var a = 1;

function logA() {
  if (a<11) {
    console.log(a);
    a++;
    logA();
  }
  else {
    return;
  }
}

logA();
console.log("after recursion");

【讨论】:

  • 为什么说没有递归呢?即使它包含在另一个函数 (setTimeout) 中,logA 仍会从其自身内部调用。
  • 不,不是。 logA() 调用setTimeout(),然后返回。当计时器到期时,VM 调用logA(),但此时之前对logA() 的调用不再在调用堆栈上。这就是为什么这不符合递归的条件。
  • 明白了。谢谢。如果不是 setTimeout,而是像这样将它包装在 IIFE 中呢:(function(){logA()})();那会是递归吗?
  • 我们在这里转向半哲学领域,但我会说它在技术上是递归,因为它是相同的调用堆栈。刚查了一下维基百科,这算作“间接递归”。
【解决方案2】:

当您的logA() 函数第一次被调用时,if 块将按预期执行。第一次调用setTimeout(logA, 100);

在 Javascript 中,setTimeout() 异步运行,因此它将在堆栈中等待 100 毫秒,然后执行下一条语句 console.log("after set timeout");。在第一次超时的这个 logA 函数被执行后,触发了另一个超时,依此类推。

所以你的输出符合预期:

1
"after set timeout"
2
3
4
5
6
7
8
9
10

【讨论】:

    【解决方案3】:

    了解堆栈在 javascript 中的行为方式可能对您来说很有趣,那就是了解事件循环。在 javascript 中,首先清空堆栈,然后执行异步回调,这些回调被堆积在事件队列中。

    在您的示例中,当我们有以下代码时:

    function test () {
      console.log('first log');
    }
    
    setTimeout(test, 0);
    
    console.log('last log');

    现在函数对象测试在事件队列中通过了。堆栈清除后,此函数将被执行。

    但是如果你像这样直接执行函数:

        function test () {
          console.log('first log');
        }
    
        setTimeout(test(), 0);
    
        console.log('last log');

    现在函数代码将直接执行,函数不会在事件队列中结束。

    希望对你有帮助

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-20
      • 1970-01-01
      • 2014-12-11
      相关资源
      最近更新 更多