是否可以在 JavaScript 中链接 setTimeout 函数?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6921275/
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-25 22:20:09  来源:igfitidea点击:

Is it possible to chain setTimeout functions in JavaScript?

javascriptsettimeout

提问by xiatica

Is it possible to chain setTimoutfunctions to ensure they run after one another?

是否可以链接setTimout功能以确保它们一个接一个运行?

回答by jfriend00

Three separate approaches listed here:

这里列出了三种不同的方法:

  1. Manually nest setTimeout()callbacks.
  2. Use a chainable timer object.
  3. Wrap setTimeout()in a promise and chain promises.
  1. 手动嵌套setTimeout()回调。
  2. 使用可链接的计时器对象。
  3. 包裹setTimeout()在承诺和链承诺中。

Manually Nest setTimeout callbacks

手动嵌套 setTimeout 回调

Of course. When the first one fires, just set the next one.

当然。当第一个触发时,只需设置下一个。

setTimeout(function() {
    // do something
    setTimeout(function() {
        // do second thing
    }, 1000);
}, 1000);

Chainable Timer Object

可链接的定时器对象

You can also make yourself a little utility object that will let you literally chain things which would let you chain calls like this:

您还可以让自己成为一个小实用程序对象,它可以让您真正链接事物,让您可以像这样链接调用:

delay(fn1, 400).delay(fn2, 500).delay(fn3, 800);

function delay(fn, t) {
    // private instance variables
    var queue = [], self, timer;
    
    function schedule(fn, t) {
        timer = setTimeout(function() {
            timer = null;
            fn();
            if (queue.length) {
                var item = queue.shift();
                schedule(item.fn, item.t);
            }
        }, t);            
    }
    self = {
        delay: function(fn, t) {
            // if already queuing things or running a timer, 
            //   then just add to the queue
           if (queue.length || timer) {
                queue.push({fn: fn, t: t});
            } else {
                // no queue or timer yet, so schedule the timer
                schedule(fn, t);
            }
            return self;
        },
        cancel: function() {
            clearTimeout(timer);
            queue = [];
            return self;
        }
    };
    return self.delay(fn, t);
}

function log(args) {
    var str = "";
    for (var i = 0; i < arguments.length; i++) {
        if (typeof arguments[i] === "object") {
            str += JSON.stringify(arguments[i]);
        } else {
            str += arguments[i];
        }
    }
    var div = document.createElement("div");
    div.innerHTML = str;
    var target = log.id ? document.getElementById(log.id) : document.body;
    target.appendChild(div);
}


function log1() {
   log("Message 1");
}
function log2() {
   log("Message 2");
}
function log3() {
   log("Message 3");
}

var d = delay(log1, 500)
    .delay(log2, 700)
    .delay(log3, 600)

Wrap setTimeout in a Promise and Chain Promises

将 setTimeout 包装在 Promise 和 Chain Promise 中

Or, since it's now the age of promises in ES6+, here's similar code using promises where we let the promise infrastructure do the queuing and sequencing for us. You can end up with a usage like this:

或者,由于现在是 ES6+ 中 promise 的时代,这里是使用 promise 的类似代码,我们让 promise 基础设施为我们进行排队和排序。您最终可以使用这样的用法:

Promise.delay(fn1, 500).delay(fn2, 700).delay(fn3, 600);

Here's the code behind that:

这是背后的代码:

// utility function for returning a promise that resolves after a delay
function delay(t) {
    return new Promise(function (resolve) {
        setTimeout(resolve, t);
    });
}

Promise.delay = function (fn, t) {
    // fn is an optional argument
    if (!t) {
        t = fn;
        fn = function () {};
    }
    return delay(t).then(fn);
}

Promise.prototype.delay = function (fn, t) {
    // return chained promise
    return this.then(function () {
        return Promise.delay(fn, t);
    });

}

function log(args) {
    var str = "";
    for (var i = 0; i < arguments.length; i++) {
        if (typeof arguments[i] === "object") {
            str += JSON.stringify(arguments[i]);
        } else {
            str += arguments[i];
        }
    }
    var div = document.createElement("div");
    div.innerHTML = str;
    var target = log.id ? document.getElementById(log.id) : document.body;
    target.appendChild(div);
}

function log1() {
    log("Message 1");
}

function log2() {
    log("Message 2");
}

function log3() {
    log("Message 3");
}

Promise.delay(log1, 500).delay(log2, 700).delay(log3, 600);

The functions you supply to this version can either by synchonrous or asynchronous (returning a promise).

你提供给这个版本的函数可以是同步的,也可以是异步的(返回一个承诺)。

回答by Adam Miles Crockett

If your using Typescript targeting ES6 this is pretty simple with Async Await. This is also very very easy to read and a little upgrade to the promises answer.

如果您使用面向 ES6 的 Typescript,这对于 Async Await 来说非常简单。这也非常易于阅读,并且对 promises 答案进行了一些升级。

//WARNING: this is Typescript source code
//expect to be async
async function timePush(...arr){
    function delay(t){
        return new Promise((resolve,reject)=>{
            setTimeout(()=>{
                resolve();
            },t)
        })
    }
    //for the length of this array run a delay
    //then log, you could always use a callback here
    for(let i of arr){
        //pass the items delay to delay function
        await delay(i.time);
        console.log(i.text)
    }
}


timePush(
    {time:1000,text:'hey'},
    {time:5000,text:'you'},
    {time:1000,text:'guys'}
);

回答by Kenji

I have encountered the same issue. My solution was to call self by setTimeout, it works.

我遇到了同样的问题。我的解决方案是通过调用 self setTimeout,它有效。

let a = [[20,1000],[25,5000],[30,2000],[35,4000]];

function test(){
  let b = a.shift();
  console.log(b[0]);
  if(a.length == 0) return;
  setTimeout(test,b[1]);
}

the second element in array a is time to be delayed

数组 a 中的第二个元素是要延迟的时间

回答by Penny Liu

Inspired by @jfriend00I demonstrated a shorter version:

受到@jfriend00 的启发,我展示了一个较短的版本:

Promise.resolve()
  .then(() => delay(400))
  .then(() => log1())
  .then(() => delay(500))
  .then(() => log2())
  .then(() => delay(800))
  .then(() => log3());

function delay(duration) {
  return new Promise((resolve) => {
    setTimeout(() => resolve(), duration);
  });
}

function log1() {
  console.log("Message 1");
}

function log2() {
  console.log("Message 2");
}

function log3() {
  console.log("Message 3");
}