如何同步JavaScript的回调?(How to synch JavaScript callbac

2019-08-22 04:27发布

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

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

代码示例:

 for ... in ... { myFunc1(callback); // callbacks are executed asynchly } myFunc2(); // can only execute properly if all the myFunc1 callbacks are done 

建议的解决方案:

发起在环形保持环的长度的开始的计数器,和每个回调递减该计数器。 当计数器命中0,执行myfunc2所。 这主要是为了让回调知道,如果它在序列中的最后一个回调,如果是,请拨打myfunc2所当它这样做。

问题:

  1. 需要计数器代码中的每一个这样的序列,且无处不在无意义的柜台是不是一个好的做法。
  2. 如果你还记得如何在经典的同步问题线程冲突,当多个线程都在同一个变种叫var--,会出现不良后果。 做同样在JavaScript中发生的呢?

最终的问题:

有没有更好的解决办法?

Answer 1:

好消息是,JavaScript是单线程; 这意味着,解决方案通常会与“共享”变量工作良好,即不互斥需要锁。

如果你想序列非同步的任务,然后完成回调,你可以使用这个辅助函数:

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

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

第一个参数是值的阵列,需要在每遍要传递,第二个参数是循环回调(在下面解释),并在最后一个参数是完成回调函数。

这是循环的回调函数:

function loopFn(nextTask, value) {
    myFunc1(value, nextTask);
}

则传递的第一个参数是将执行下一个任务的功能,它的意思传递给你的非同步功能。 第二个参数是你的价值观阵列的当前条目。

让我们假设非同步任务看起来是这样的:

function myFunc1(value, callback)
{
  console.log(value);
  callback();
}

它打印的价值,此后,它调用回调; 简单。

然后,设置在运动的整个事情:

serializeTasks([1,2, 3], loopFn, function() {
    console.log('done');
});

演示

并行它们,你需要不同的功能:

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

    arr.forEach(function(value) {
      fn(doneTask, value);
    });
}

和你的循环功能,将是(唯一的参数名称更改):

function loopFn(doneTask, value) {
    myFunc1(value, doneTask);
}

演示



Answer 2:

The second problem is not really a problem as long as every one of those is in a separate function and the variable is declared correctly (with var); local variables in functions do not interfere with each other.

The first problem is a bit more of a problem. Other people have gotten annoyed, too, and ended up making libraries to wrap that sort of pattern for you. I like async. With it, your code might look like this:

async.each(someArray, myFunc1, myFunc2);

It offers a lot of other asynchronous building blocks, too. I'd recommend taking a look at it if you're doing lots of asynchronous stuff.



Answer 3:

您可以通过使用一个jQuery Deferred对象实现这一目标。

var deferred = $.Deferred();
var success = function () {
    // resolve the deferred with your object as the data
    deferred.resolve({
        result:...;
    });
};


Answer 4:

有了这个辅助函数:

function afterAll(callback,what) {
  what.counter = (what.counter || 0) + 1;
  return function() {
    callback(); 
    if(--what.counter == 0) 
      what();
  };
}

你的循环将如下所示:

function whenAllDone() { ... }
for (... in ...) {
  myFunc1(afterAll(callback,whenAllDone)); 
}

这里afterAll创造了回调代理功能,它也递减计数器。 并调用whenAllDone功能时,所有的回调完成。



Answer 5:

单个线程并不总是保证。 不要把它错了。

案例1:例如,如果我们有2个功能如下。

var count=0;
function1(){
  alert("this thread will be suspended, count:"+count);
}
function2(){
  //anything
  count++;
  dump(count+"\n");
}

然后功能1返回前,函数2也将被调用时,如果1个线程保证,那么函数2不会FUNCTION1返回之前调用。 你可以试试这个。 你会发现数量正在上升,而你是被警告。

案例2:使用Firefox,Chrome的代码,前1点函数返回(无警报内),其他功能也可以被调用。

因此,我们确实需要一个互斥锁。



Answer 6:

有很多很多的方式来实现这一点,我希望这些建议帮助!

首先,我将改变回调到一个承诺! 这里是做的一个方法:

function aPromise(arg) {
    return new Promise((resolve, reject) => {
        aCallback(arg, (err, result) => {
            if(err) reject(err);
            else resolve(result);
        });
    })
}

接下来,使用减少由一个处理一个阵列的一个的元素!

const arrayOfArg = ["one", "two", "three"];
const promise = arrayOfArg.reduce(
    (promise, arg) => promise.then(() => aPromise(arg)), // after the previous promise, return the result of the aPromise function as the next promise
    Promise.resolve(null) // initial resolved promise
    );
promise.then(() => {
    // carry on
});

如果你想在同一时间处理一个数组的所有元素,使用地图的Promise.all!

const arrayOfArg = ["one", "two", "three"];
const promise = Promise.all(arrayOfArg.map(
    arg => aPromise(arg)
));
promise.then(() => {
    // carry on
});

如果你能使用异步/等待那么你可以只是简单的做到这一点:

const arrayOfArg = ["one", "two", "three"];
for(let arg of arrayOfArg) {
    await aPromise(arg); // wow
}

// carry on

你甚至可以用我的非常酷的同步,异步库是这样的:

const arrayOfArg = ["one", "two", "three"];
const context = {}; // can be any kind of object, this is the threadish context

for(let arg of arrayOfArg) {
    synchronizeCall(aPromise, arg); // synchronize the calls in the given context
}

join(context).then(() => { // join will resolve when all calls in the context are finshed
    // carry on
});

最后但并非最不重要的,用细异步库,如果你真的不想使用的承诺。

const arrayOfArg = ["one", "two", "three"];
async.each(arrayOfArg, aCallback, err => {
    if(err) throw err; // handle the error!
    // carry on
});


文章来源: How to synch JavaScript callbacks?