如何强制执行由setTimeout排队的0延迟任务的执行顺序



我需要顺序调用一些函数,为了强制框架在每个步骤后进行脏检查,我使用了setTimeout(func,0)来触发脏检查机制。

我知道简单地一个接一个地调用setTimeout并不能保证传递的异步函数会按照预期的顺序被调用,因此我拼凑了以下解决方案:

function foo(arg){
setTimeout(()=>console.log('executing task' + arg),0);
console.log('on call stack' + arg);
return foo;
}

我试过foo(1)(2)(3)(4)(5),效果很好。但我不确定它是否总是能正常工作。

有人能帮我吗!


@Steffemio的答案肯定可以使排队的任务具有确定性,它还确保每个任务都有自己的事件循环。

这是我的改编版本:

function queueTask(task) {
var queue = [];
function nextTask() {
setTimeout(function () {
queue.length && queue.shift()(); taskCount++;
queue.length && nextTask();
}, 0);
}
return (function pushTask(task) {
queue.push(task);
//After the first call trigger the timeout asynchrony
if (queue.length === 1) { nextTask(); } 
return pushTask;
})(task);
}
//Test part below
function t(arg) { return function () { console.log('Task ' + arg); } }
var taskCount = 0;
var beginTime = Date.now();
queueTask(t(1))(t(2))(t(3))(t(4))(t(5))
(t('a'))(t('b'))(t('c'))(t('d'))(t('e'))
(t(1))(t(2))(t(3))(t(4))(t(5))
(t('a'))(t('b'))(t('c'))(t('d'))(t('e'))  
(function () { console.log(taskCount + ' tasks executed, Time elapsed: ' + (Date.now() - beginTime)); });

经过一番研究,我了解到传递给setTimeout的回调只有在调用堆栈被清除(上面没有更多代码)时才会被系统调用,因此排队任务的实际执行要等到排队完成后才能开始,如果我们将几个0延迟超时任务线性排队,当下一个事件循环开始时,它们都将在一次运行中执行。那不是我想要的!因此,在前面的setTimeout的回调中调用setTimeout是迄今为止我所知道的强制执行逐点任务调度的唯一方法。

为了更好地理解,请参考Philip Roberts在JSConf EU 2014 上的演讲"无论如何,事件循环到底是什么">

也许这种方法适用于

function foo(args) {
return new Promise((resolve, reject) => {
// your code based on args
if (args.length) {
console.log("processing", args[0]);
resolve(args.slice(1, args.length));
} else {
reject("finished");
}
}).then(args => {
return foo(args);
}).catch(() => {
console.log("Tasks completed...");
});
}
foo([1, 2, 3 ,4, 5]);

输出应该类似于:

processing 1
processing 2
processing 3
processing 4
processing 5
Tasks completed...

您需要一个队列:

q = [];
addQueue(1)(2)(3)(4)(5);
addQueue('a')('b')('d');
addQueue(1)(2)(3)(4)(5);
addQueue('a')('b')('d');
addQueue(1)(2)(3)(4)(5);
addQueue('a')('b')('d');
function queue(){
if(q.length){
setTimeout(function(){
console.log('queue: ' + q.shift()); 
q.length && queue();
}, 1000);
}
}
function addQueue(n){
if(q.length){
q.push(n);
}else{
q.push(n);
queue();
}
return addQueue;
}

用于测试将代码复制粘贴到控制台。

最新更新