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

时间:2016-06-03 11:26:47

标签: javascript settimeout

我需要按顺序调用某些函数,并且为了强制框架在每一步之后执行脏检查,我使用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),它运行正常。但我不确定它是否会始终正常工作。

任何人都可以帮助我!

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

这是我的改编版本:

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上发表的演讲"What the heck is the event loop anyway"

2 个答案:

答案 0 :(得分:2)

可能这种方法适合你

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...

答案 1 :(得分:1)

你需要一个队列:

//CREATOR OBJECT
using UnityEngine;
using System.Collections;

public class createProjectile : MonoBehaviour
{

    public GameObject projectile;

    void OnMouseDown()
    {
        Instantiate(projectile);
    }
}

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