javascript 嵌套 setTimeout 替代方案?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17574571/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 08:50:14  来源:igfitidea点击:

Nested setTimeout alternative?

javascript

提问by Royi Namir

I need to execute 3 functions in a 1 sec delay.

我需要在 1 秒的延迟内执行 3 个函数。

for simplicity those functions are :

为简单起见,这些功能是:

console.log('1');
console.log('2');
console.log('3');

I could do this: ( very ugly)

我可以这样做:(非常丑陋)

 console.log('1')
 setTimeout(function () {
     setTimeout(function () {
         console.log('2')
         setTimeout(function () {
             console.log('3')

         }, 1000)
     }, 1000)

 }, 1000)

Or I could create an arrayof functions and use setIntervalwith globalcounter.

或者我可以创建一个array函数并setIntervalglobal计数器一起使用。

Is there any elegantway of doing this ?

有没有 优雅的方法来做到这一点?

(p.s. function no.2 is not dependent on function number 1... hence - every sec execute the next function.).

(ps 函数 2 不依赖于函数 1 ......因此 - 每秒执行下一个函数。)。

采纳答案by Royi Namir

You can use something like this with setTimeout:

你可以使用这样的东西setTimeout

var funcs = [func1, func2, func3],
    i = 0;

function callFuncs() {
    funcs[i++]();
    if (i < funcs.length) setTimeout(callFuncs, 1000);
}
setTimeout(callFuncs, 1000); //delay start 1 sec.

or start by just calling callFuncs() directly.

或者直接调用 callFuncs() 开始。

Update

更新

An setIntervalapproach (be aware of the risk of call stacking):

一种setInterval方法(注意调用堆栈的风险):

var funcs = [func1, func2, func3],
    i = 0,
    timer = setInterval(callFuncs, 1000);

function callFuncs() {
    funcs[i++]();
    if (i === funcs.length) clearInterval(timer);
}

回答by Reason

Assuming you run it on a modern browser or have added support for array.map this is quite concise:

假设您在现代浏览器上运行它或添加了对 array.map 的支持,这是非常简洁的:

[func1, func2, func3].map(function (fun, index) {
    setTimeout(fun, 1000 + index * 1000);
}

回答by Salketer

setTimeout(function(){console.log('1')}, 1000);
setTimeout(function(){console.log('2')}, 2000);
setTimeout(function(){console.log('3')}, 3000);

回答by John

There is a new type of function declaration called generators in es6 (a.k.a ecmascript 6, es2015). It is incredibly useful for this situation, and makes your async code look synchronous. es6 is the latest standard of JavaScript as of 2015. It works on modern browsers but you can use Babeland its javascript polyfill to use generators now even on older browsers.

在 es6(又名 ecmascript 6,es2015)中有一种新的函数声明类型,称为生成器。它在这种情况下非常有用,并使您的异步代码看起来是同步的。es6 是 2015 年最新的 JavaScript 标准。它适用于现代浏览器,但您现在可以使用Babel及其 javascript polyfill 来使用生成器,即使在旧浏览器上也是如此。

Hereis a tutorial on generators.

是关于生成器的教程。

The function myDelayedMessages below is an example of a generator. Run is a helper function that takes a generator function which it calls and provides a function to advance the generator as the first argument of the generator function that it called.

下面的函数 myDelayedMessages 是一个生成器的例子。Run 是一个辅助函数,它接受一个它调用的生成器函数,并提供一个函数来推进生成器作为它调用的生成器函数的第一个参数。

function delay(time, callback) {
      setTimeout(function () {
        callback();
      }, time);
}

function run(generatorFunction) {
  var generatorItr = generatorFunction(resume);
  function resume(callbackValue) {
    generatorItr.next(callbackValue);
  }
  generatorItr.next()
}

run(function* myDelayedMessages(resume) {
  for(var i = 1; i <= 3; ++i) {
    yield delay(1000, resume);
    console.log(i);
  }
});

This is an overview of the code which is similar to the above tutorial's final overview.

这是代码的概述,类似于上述教程的最终概述。

  1. runtakes our generator and creates a resume function. run creates a generator-iterator object (the thing you call next on), providing resume.
  2. Then it advances the generator-iterator one step to kick everything off.
  3. Our generator encounters the first yield statement and calls delay.
  4. Then the generator pauses.
  5. delay completes 1000ms later and calls resume.
  6. resume tells our generator to advance a single step.
  7. Our generator continues from the spot it yielded at then console.logs i, which is 1, then continues the loop
  8. Our generator encounters the second call to yield, calls delay and pauses again.
  9. delay waits 1000ms and ultimately calls the resume callback. resume advances the generator again.
  10. Our generator continues from the spot it yielded at then console.logs i, which is 2, then continues the loop.
  11. delay waits 1000ms and ultimately calls the resume callback. resume advances the generator again.
  12. Our generator continues from the spot it yielded at then console.logs i, which is 3, then continues and the loop finishes.
  13. There are no more calls to yield, the generator finishes executing.
  1. run获取我们的生成器并创建一个恢复函数。run 创建一个生成器-迭代器对象(你接下来调用的东西),提供简历。
  2. 然后它将生成器-迭代器推进一步以启动一切。
  3. 我们的生成器遇到第一个 yield 语句并调用延迟。
  4. 然后发电机暂停。
  5. 延迟 1000 毫秒后完成并调用恢复。
  6. resume 告诉我们的生成器前进一步。
  7. 我们的生成器从它在 console.logs i 产生的地方继续,它是 1,然后继续循环
  8. 我们的生成器遇到了第二次对 yield 的调用,调用了 delay 并再次暂停。
  9. delay 等待 1000 毫秒并最终调用恢复回调。resume 再次推进发电机。
  10. 我们的生成器从它在 console.logs i 处产生的位置继续,即 2,然后继续循环。
  11. delay 等待 1000 毫秒并最终调用恢复回调。resume 再次推进发电机。
  12. 我们的生成器从它在 then console.logs i 处产生的位置继续,即 3,然后继续,循环结束。
  13. 不再有对 yield 的调用,生成器完成执行。

回答by GameAlchemist

I think most simple way to do this is to create some closures within a function.
First i'll recall that you have big interest in using setInterval, since the overhead of the setTimeoutmight have it trigger 10ms off target. So especially if using short (<50ms) interval, prefer setInterval. So we need to store the function array, the index of latest executed function, and an interval reference to stop the calls.

我认为最简单的方法是在函数中创建一些闭包。
首先,我会记得您对使用 非常感兴趣setInterval,因为 的开销setTimeout可能会触发 10 毫秒的偏离目标。所以特别是如果使用短(<50ms)间隔,更喜欢 setInterval。所以我们需要存储函数数组,最近执行的函数的索引,以及停止调用的区间引用。

function chainLaunch(funcArray, time_ms) {
  if (!funcArray || !funcArray.length) return;
  var fi = 0; // function index
  var callFunction = function () {
      funcArray[fi++]();
      if (fi==funcArray.length)
              clearInterval(chainInterval);
   } ;
  var chainInterval = setInterval( callFunction, time_ms);
}

Rq : You might want to copy the function array ( funcArray = funcArray.slice(0);)
Rq2 : You might want to loop within the array
Rq3 : you might want to accept additionnal arguments to chainlaunch. retrieve them with var funcArgs = arguments.slice(3);and use apply on the functions : funcArray[fi++].apply(this,funcArgs);

Rq:您可能希望复制函数数组 ( funcArray = funcArray.slice(0);)
Rq2:您可能希望在数组内循环
Rq3:您可能希望接受 chainlaunch 的附加参数。检索它们var funcArgs = arguments.slice(3);并在函数上使用 apply :funcArray[fi++].apply(this,funcArgs);

Anyway the following test works :

无论如何,以下测试有效:

var f1 = function() { console.log('1'); };
var f2 = function() { console.log('2'); };
var f3 = function() { console.log('3'); };

var fArr = [f1, f2, f3];

chainLaunch(fArr, 1000);

as you can see in this fiddle : http://jsfiddle.net/F9UJv/1/(open the console)

正如你在这个小提琴中看到的:http: //jsfiddle.net/F9UJv/1/(打开控制台)

回答by Fran Cano

There are here two methods. One with setTimeout and anotherone with setInterval. The first one is better in my opinion.

这里有两种方法。一个带有 setTimeout,另一个带有 setInterval。第一个在我看来更好。

for(var i = 1; i++; i<=3) {
  setTimeout(function() {
      console.log(i);
    }, 1000*i);
}
// second choice:
var i = 0;
var nt = setInterval(function() {
      if(i == 0) return i++;
      console.log(i++);
      if(i>=3) clearInterval(nt);
    }, 1000);