如何同步JavaScript回调?

Xav*_*_Ex 12 javascript multithreading synchronization callback

我在JavaScript开发了相当长的一段时间,但仍然净牛仔开发商,如的很多事情总是困扰着我的synching JavaScript的回调之一.

我将描述一个普通的场景时,这一问题将得到提升:我有一大堆的操作由一个for循环执行多次,并且每个操作都有一个回调.在for循环之后,我需要执行另一个操作,但是如果完成了for循环的所有回调,则此操作只能成功执行.

代码示例:

 for ... in ... {
   myFunc1(callback); // callbacks are executed asynchly
 }

 myFunc2(); // can only execute properly if all the myFunc1 callbacks are done
Run Code Online (Sandbox Code Playgroud)

建议的解决方案:

在循环开始时启动一个计数器,保持循环的长度,并且每个回调递减该计数器.当计数器达到0时,执行myFunc2.这实际上是让回调知道它是否是序列中的最后一个回调,如果是,则在完成时调用myFunc2.

问题:

  1. 代码中的每个这样的序列都需要一个计数器,并且在任何地方都有无意义的计数器并不是一个好习惯.
  2. 如果你还记得经典同步问题中线程如何冲突,当多个线程都在同一个var上调用var--时,会发生不希望的结果.JavaScript中也会发生同样的情况吗?

终极问题:

有更好的解决方案吗?

Ja͢*_*͢ck 12

好消息是JavaScript是单线程的; 这意味着解决方案通常可以很好地与"共享"变量一起使用,即不需要互斥锁.

如果要序列化异步任务,然后执行完成回调,可以使用此辅助函数:

function serializeTasks(arr, fn, done)
{
    var current = 0;

    fn(function iterate() {
        if (++current < arr.length) {
            fn(iterate, arr[current]);
        } else {
            done();
        }
    }, arr[current]);
}
Run Code Online (Sandbox Code Playgroud)

第一个参数是需要在每次传递中传递的值数组,第二个参数是循环回调(如下所述),最后一个参数是完成回调函数.

这是循环回调函数:

function loopFn(nextTask, value) {
    myFunc1(value, nextTask);
}
Run Code Online (Sandbox Code Playgroud)

传递的第一个参数是一个执行下一个任务的函数,它意味着传递给你的异步函数.第二个参数是值数组的当前条目.

我们假设asynch任务看起来像这样:

function myFunc1(value, callback)
{
  console.log(value);
  callback();
}
Run Code Online (Sandbox Code Playgroud)

它打印值,然后调用回调; 简单.

然后,设置整个运动:

serializeTasks([1,2, 3], loopFn, function() {
    console.log('done');
});
Run Code Online (Sandbox Code Playgroud)

演示

要并行化它们,您需要一个不同的功能:

function parallelizeTasks(arr, fn, done)
{
    var total = arr.length,
    doneTask = function() {
      if (--total === 0) {
        done();
      }
    };

    arr.forEach(function(value) {
      fn(doneTask, value);
    });
}
Run Code Online (Sandbox Code Playgroud)

你的循环函数将是这个(只有参数名称更改):

function loopFn(doneTask, value) {
    myFunc1(value, doneTask);
}
Run Code Online (Sandbox Code Playgroud)

演示