我怎么能等待一组异步回调函数?

时间:2012-04-04 02:14:00

标签: javascript asynchronous

我的代码在javascript中看起来像这样:

forloop {
    //async call, returns an array to its callback
}

完成所有这些异步调用后,我想计算所有数组的最小值。

我如何等待所有这些?

我现在唯一的想法就是有一个名为done的布尔数组,并在第i个回调函数中将done [i]设置为true,然后说while(并非所有都已完成){}

编辑:我想一个可能的,但丑陋的解决方案是在每个回调中编辑完成的数组,然后如果从每个回调设置所有其他完成后调用一个方法,那么最后一个回调完成将调用continue方法

提前致谢。

7 个答案:

答案 0 :(得分:180)

你的代码并不是很具体,所以我会编写一个场景。假设您有10个ajax调用,并且您希望累积来自这10个ajax调用的结果,然后当它们全部完成时您想要执行某些操作。您可以通过在数组中累积数据并跟踪最后一个数据的完成时间来完成此操作:

手动计数器

var ajaxCallsRemaining = 10;
var returnedData = [];

for (var i = 0; i < 10; i++) {
    doAjax(whatever, function(response) {
        // success handler from the ajax call

        // save response
        returnedData.push(response);

        // see if we're done with the last ajax call
        --ajaxCallsRemaining;
        if (ajaxCallsRemaining <= 0) {
            // all data is here now
            // look through the returnedData and do whatever processing 
            // you want on it right here
        }
    });
}

注意:错误处理在这里很重要(未显示,因为它特定于您如何进行ajax调用)。当一个ajax调用永远不会完成时,您将需要考虑如何处理这种情况,无论是出错还是长时间停滞或长时间停留都会。


jQuery承诺

在2014年添加我的答案。现在,承诺通常用于解决此类问题,因为jQuery的$.ajax()已经返回一个承诺,而$.when()会告诉您何时一组承诺是全部已解决并将为您收集退货结果:

var promises = [];
for (var i = 0; i < 10; i++) {
    promises.push($.ajax(...));
}
$.when.apply($, promises).then(function() {
    // returned data is in arguments[0][0], arguments[1][0], ... arguments[9][0]
    // you can process it here
}, function() {
    // error occurred
});

ES6标准承诺

As specified in kba's answer:如果你有一个内置本机promises的环境(现代浏览器或node.js或使用babeljs transile或使用promise polyfill),那么你可以使用ES6指定的promises。有关浏览器支持,请参阅this table。几乎所有当前浏览器都支持Promise,IE除外。

如果doAjax()返回承诺,那么您可以执行此操作:

var promises = [];
for (var i = 0; i < 10; i++) {
    promises.push(doAjax(...));
}
Promise.all(promises).then(function() {
    // returned data is in arguments[0], arguments[1], ... arguments[n]
    // you can process it here
}, function(err) {
    // error occurred
});

如果您需要对返回承诺的异步操作进行非承诺异步操作,您可以像这样“宣传”它:

function doAjax(...) {
    return new Promise(function(resolve, reject) {
        someAsyncOperation(..., function(err, result) {
            if (err) return reject(err);
            resolve(result);
        });
    });
}

然后使用上面的模式:

var promises = [];
for (var i = 0; i < 10; i++) {
    promises.push(doAjax(...));
}
Promise.all(promises).then(function() {
    // returned data is in arguments[0], arguments[1], ... arguments[n]
    // you can process it here
}, function(err) {
    // error occurred
});

蓝鸟承诺

如果您使用功能更丰富的库,例如Bluebird promise library,那么它内置了一些额外的功能,可以让您更轻松:

 var doAjax = Promise.promisify(someAsync);
 var someData = [...]
 Promise.map(someData, doAjax).then(function(results) {
     // all ajax results here
 }, function(err) {
     // some error here
 });

答案 1 :(得分:14)

从2015年开始办理登机手续:我们现在在native promises {Edge 3,Firefox 40,Chrome 43,Safari 8,Opera 32和Android浏览器4.4.4和iOS Safari 8.4中most recent browser,但不是Promise.all Internet Explorer,Opera Mini和旧版Android。

如果我们想要执行10个异步操作并在完成所有操作后得到通知,我们可以使用本机mimic this,而无需任何外部库:

function asyncAction(i) {
    return new Promise(function(resolve, reject) {
        var result = calculateResult();
        if (result.hasError()) {
            return reject(result.error);
        }
        return resolve(result);
    });
}

var promises = [];
for (var i=0; i < 10; i++) {
    promises.push(asyncAction(i));
}

Promise.all(promises).then(function AcceptHandler(results) {
    handleResults(results),
}, function ErrorHandler(error) {
    handleError(error);
});

答案 2 :(得分:10)

您可以使用jQuery的Deferred对象以及when方法。

deferredArray = [];
forloop {
    deferred = new $.Deferred();
    ajaxCall(function() {
      deferred.resolve();
    }
    deferredArray.push(deferred);
}

$.when(deferredArray, function() {
  //this code is called after all the ajax calls are done
});

答案 3 :(得分:8)

你可以这样模仿它:

  countDownLatch = {
     count: 0,
     check: function() {
         this.count--;
         if (this.count == 0) this.calculate();
     },
     calculate: function() {...}
  };

然后每个异步调用执行此操作:

countDownLatch.count++;

在方法结束时的每个异步回调中添加以下行:

countDownLatch.check();

换句话说,您可以模拟倒计时锁存功能。

答案 4 :(得分:4)

在我看来,这是最简洁的方式。

Promise.all

FetchAPI

(由于某种原因,Array.map不在我的内部工作。但是你可以使用.forEach和[] .concat()或类似的东西)

Promise.all([
  fetch('/user/4'),
  fetch('/user/5'),
  fetch('/user/6'),
  fetch('/user/7'),
  fetch('/user/8')
]).then(responses => {
  return responses.map(response => {response.json()})
}).then((values) => {
  console.log(values);
})

答案 5 :(得分:1)

使用像after

这样的控制流程库
after.map(array, function (value, done) {
    // do something async
    setTimeout(function () {
        // do something with the value
        done(null, value * 2)
    }, 10)
}, function (err, mappedArray) {
    // all done, continue here
    console.log(mappedArray)
})

答案 6 :(得分:0)

在 Node.js 中你可以使用 async/await 来控制异步流程

  • 在 Node.js 7.6 中支持 async/await
  • 在 Node.js v8 中支持用于承诺回调的 util 函数

示例代码:

const foo = async () => {
  try {
    const ids = [100, 101, 102];
    const fetchFromExternalApi = util.promisify(fetchFromExternalApiCallback);
    const promises = ids.map((id) => fetchFromExternalApi(id));
    const dataList = await Promise.resolve(promises); // dataList is an array
    return dataList;
  } catch (err) {
    // error handling
  }
};
相关问题