4个定时器同步执行

        setTimeout(function(){console.log(1)},1000),
        setTimeout(function(){console.log(2)},5000),
        setTimeout(function(){console.log(3)},300),
        setTimeout(function(){console.log(4)},800)

我有4个定时器,想按照顺序执行,怎么实现呢

阅读 3.9k
5 个回答

你想要按照什么顺序执行,想要按照1,2,3,4这种执行吗?

这种方式对你们前端来说的话,可以用Promise,不用这个自己实现一个也没啥问题啊,我随便写个例子:

var callbacks = [
    [function () {
        console.log(1)
    }, 1000],
    [function () {
        console.log(2)
    }, 5000],
    [function () {
        console.log(3)
    }, 300],
    [function () {
        console.log(4)
    }, 800],
];

function doTimeout(callbacks) {
    var callback = callbacks.shift()
    setTimeout(function () {
        callback[0]();
        if (callbacks.length) {
            doTimeout(callbacks);
        }
    }, callback[1]);
}
doTimeout(callbacks);

(我觉得你可能想要的是按顺序执行一些异步操作)

想要顺序持续,首先第一涉及到了等待,即 async/await,于是需要用 Promise 封装,第二点涉及到 Promise 顺序执行,通常使用 Array.reduce 来实现:

const queue = [
  () => new Promise(r => setTimeout(() => r(), 1000)),
  () => new Promise(r => setTimeout(() => r(), 5000)),
  () => new Promise(r => setTimeout(() => r(), 300)),
  () => new Promise(r => setTimeout(() => r(), 800))
]

await queue.reduce(async (memo, process, i) => {
 await memo
 await process()
 console.log(i + 1)
}, undefined)

image.png

1.启动定时器
2.定时器到时间任务执行

启动四个定时器的顺序
四个定时器的任务执行顺序

定时器之所以叫定时器,是针对时间的,时间上有顺序,任务的执行就有顺序。既然规定了时间上的顺序,有要求不按事件顺序,那用定时器干嘛呢。要么不用定时器,要么用时间控制顺序。

要想让延时定时器按1234的打印顺序执行的话有以下几个方法:

  1. 将延时定时器函数封装成Promise对象并使用async/await语法同步调用

    (async() => {
     const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
     await delay(1000);
     console.log(1);
     await delay(5000);
     console.log(2);
     await delay(300);
     console.log(3);
     await delay(800);
     console.log(4);
    })();
  2. 将定时器回调函数放在队列中封装后递归执行

    function queue() {
     var args = arguments;
     if (!args.length) return;
     var options = Array.prototype.shift.call(args);
     var that = this;
     setTimeout(function() {
         options.callback.call(that);
         queue.apply(that, args);
     }, options.delay);
    }
    queue({
     callback: function() {
         console.log(1);
     },
     delay: 1000
    }, {
     callback: function() {
         console.log(2);
     },
     delay: 5000
    }, {
     callback: function() {
         console.log(3);
     },
     delay: 300
    }, {
     callback: function() {
         console.log(4);
     },
     delay: 800
    });
  3. 同样是先将定时器回调函数放入队列,然后把每个定时器的延时时间叠加后执行以达到按顺序调用的目的

    function queue() {
     var delay = 0;
     var that = this;
     for (var i = 0; i < arguments.length; ++i) {
         delay += arguments[i].delay;
         setTimeout(function(callback) {
             callback.call(that);
         }, delay, arguments[i].callback);
     }
    }
    queue({
     callback: function() {
         console.log(1);
     },
     delay: 1000
    }, {
     callback: function() {
         console.log(2);
     },
     delay: 5000
    }, {
     callback: function() {
         console.log(3);
     },
     delay: 300
    }, {
     callback: function() {
         console.log(4);
     },
     delay: 800
    });

采用纯 setTimeout 实现,大概应该这么写

setTimeout(function () {
    console.log(1);
    setTimeout(function () {
        console.log(2);
        setTimeout(function () {
            console.log(3);
            setTimeout(function () {
                console.log(4);
            }, 800);
        }, 300);
    }, 5000);
}, 1000);

也就是在每次 timeout 到时间的时候启动下一个 timer。

实际每次调用的行为基本致,如果封装成 Promise,可能更容易看一些

function runAfter(fn, milliseconds) {
    return new Promise(resolve => {
        setTimeout(() => resolve(fn()), milliseconds);
    });
}

runAfter(() => console.log(1), 1000)
    .then(() => runAfter(() => console.log(2), 5000))
    .then(() => runAfter(() => console.log(3), 300))
    .then(() => runAfter(() => console.log(4), 800));

不过既然都封闭成 Promise 了,不如用 await 看起来更清晰

// 多数情况下,await 必须在 async 函数中使用,所以封个 IIFE。
// 注支持顶层 await 的环境不需要如此
(async () => {
    await runAfter(() => console.log(1), 1000);
    await runAfter(() => console.log(2), 5000);
    await runAfter(() => console.log(3), 300);
    await runAfter(() => console.log(4), 800);
})();
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题