【问题标题】:JS setTimeout() repeats, isn't loop better than recursion syntax to prevent stack exhaustion?JS setTimeout() 重复,循环不是比递归语法更好地防止堆栈耗尽吗?
【发布时间】:2017-11-30 22:47:25
【问题描述】:

在前一个回调结束后恰好一秒重复执行回调(几乎)的标准方法是(所谓的)“递归”语法:

function callback_recurse(){
   // do something, if <condtion> break
   setTimeout(callback_recurse, 1000); 
}
setTimeout(callback_recurse, 1000); // first call

为什么不使用循环语法呢?:

function callback(){
   // do something 
}
function loopcallbacks(){
    while (<condition>)
        setTimeout(callback, 1000);
}

毕竟,使用迭代循环语法永远不会用完堆栈空间,就像使用递归语法一样。

(免责声明:我正在回答我自己的问题)

【问题讨论】:

  • 我不确定我是否理解为什么第二个示例甚至会被视为一个选项,因为它是一个无限循环。
  • 第一个 sn-p 不是递归的。它是伪递归的,我不希望它增加调用堆栈。同样在第二个 sn-p 中,您将同步推送无数回调,以便在将它们插入事件循环后 1000 毫秒执行。

标签: javascript loops events recursion settimeout


【解决方案1】:

loopcallbacks() 内的循环中使用 setTimeout() 将不会按预期运行,如果您预计每个任务之间会有一秒的间隔。假设您的程序中有 5 次迭代的循环,而不是无限循环。 然后整个循环在一毫秒内执行完毕,5个任务中的每一个都被安排在一秒后放入同步的JS事件队列中。它们将按顺序连续运行,但不是每一个都间隔一秒钟。如果它是一个无限循环,因为您的程序将阻塞并且堆栈将在执行任何任务之前溢出。

您不必担心使用 callback_recurse() 中所谓的“递归”语法会耗尽堆栈空间。实际上它不是真正的递归,因为调用函数在新调度的任务运行之前返回,因此堆栈不会增加大小。因此,使用所谓的“递归”语法永远不会耗尽堆栈空间。

以下程序演示了循环和递归语法:

首先,运行循环语法。输出格式为

([index], time from start, time from previous task).  

如您所见,所有 5 个任务都在运行循环后大约 1 秒运行。 (但是请注意,它们以正确的顺序连续运行 - 它们不是异步运行)。

其次,运行所谓的递归语法。输出格式是相同的,这一次结果匹配所需的行为。

function time_ms() {
  var d = new Date();
  var t = d.getTime();
  return t;
}
function makestr(d0,dlast,d,idx){
  var s0,s1;
  if (idx === 0) s0 = "*"; else s0 = (d-dlast).toString();
  s1 = (d-d0).toString()
	var s = "([" + idx.toString() + "]," + s0 + "," + s1 + "), ";
  return s;
}

function run1_sub(i,is_last){
    setTimeout(function() {
      d = time_ms();
      s = makestr(d0,dlast,d,i);
      dlast = d;
      document.getElementById("1").innerHTML += s;
      // maybe mixing metaphors, but this ensures that run2 starts after run1 finishes
      if (is_last)
        setTimeout(run2(),0) 
    }, 1000);
}

function run1() {
  var s = "";
  var i;
  var d0 = time_ms();
  var dlast = d0;
  for (i = 0; i < 5; i += 1) {
  	run1_sub(i,i==4);
  }
}
var j = 0;
var d0 = time_ms();
var dlast = d0;

function run2() {
  var s = "";
  d = time_ms();
  s = makestr(d0,dlast,d,j);
  dlast = d;
  document.getElementById("2").innerHTML += s;
  j += 1;
  if (j < 5) 
    setTimeout(run2, 1000);
}

run1();  // run2() is first called from insde the last callback of run1()
<div id="1">
</div>
<div id="2">
</div>

【讨论】:

  • 没有回调永远不会运行。 while(true) 正在阻塞
  • @Jonasw ,无限循环 setTimeouts() 是真的。
猜你喜欢
  • 2021-02-04
  • 1970-01-01
  • 2023-04-03
  • 2014-12-10
  • 2020-07-20
  • 1970-01-01
  • 2012-06-30
  • 1970-01-01
  • 2014-01-17
相关资源
最近更新 更多