Подтвердить что ты не робот

Как я могу ждать набора асинхронных функций обратного вызова?

У меня есть код, который выглядит примерно так в javascript:

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

После завершения всех этих асинхронных вызовов я хочу рассчитать min по всем массивам.

Как я могу ждать их всех?

Моя единственная идея прямо сейчас состоит в том, чтобы иметь массив логических выражений, вызванных done, и установить done [i] в ​​true в i-й функции обратного вызова, а затем сказать while (не все сделано) {}

edit: Я предполагаю, что одно возможное, но уродливое решение - это отредактировать обработанный массив в каждом обратном вызове, а затем вызвать метод, если все остальные сделанные заданы из каждого обратного вызова, таким образом, последний обратный вызов для завершения вызовет продолжающийся метод.

Спасибо заранее.

4b9b3361

Ответ 1

Вы не очень конкретно относитесь к своему коду, поэтому я создам сценарий. Скажем, у вас есть 10 вызовов ajax, и вы хотите аккумулировать результаты этих 10 аякс-звонков, а затем, когда они все закончили, вы хотите что-то сделать. Вы можете сделать это, как это, накапливая данные в массиве и отслеживая, когда последний закончен:

Ручной счетчик

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 никогда не завершается, либо с ошибкой, либо застрял в течение длительного времени или времени после долгого времени.


jQuery Promises

Добавление к моему ответу в 2014 году. В эти дни promises часто используются для решения этой проблемы, поскольку jQuery $.ajax() уже возвращает обещание, а $.when() сообщит вам, когда группа из promises все разрешено и будет собирать результаты возврата для вас:

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 Promises

Как указано в ответе kba: если у вас есть среда с встроенным встроенным promises встроенным (современный браузер или node.js или с помощью babeljs transpile или используя обещание polyfill), вы можете использовать ES6-указанный promises. См. эту таблицу для поддержки браузера. promises поддерживаются практически во всех текущих браузерах, кроме 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 Promises

Если вы используете более богатую функциональными возможностями библиотеку, такую ​​как библиотека обещаний Bluebird, у нее есть некоторые дополнительные функции, встроенные, чтобы сделать это проще:

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

Ответ 2

Регистрация с 2015 года: теперь native promises в самый последний браузер (Edge 12, Firefox 40, Chrome 43, Safari 8, Opera 32 и Android 4.4.4 и iOS Safari 8.4, но не Internet Explorer, Opera Mini и более старые версии Android).

Если мы хотим выполнить 10 асинхронных действий и получить уведомление, когда они все закончили, мы можем использовать собственный Promise.all, без любые внешние библиотеки:

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);
});

Ответ 3

Вы можете использовать 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
});

Ответ 4

Вы можете эмулировать его следующим образом:

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

то каждый асинхронный вызов делает это:

countDownLatch.count++;

в то время как в каждом асинхронном вызове в конце метода вы добавляете эту строку:

countDownLatch.check();

Другими словами, вы эмулируете функцию обратного отсчета.

Ответ 5

Это самый опрятный способ, на мой взгляд.

Promise.all

FetchAPI

(по какой-то причине Array.map не работает внутри. then функций для меня. Но вы можете использовать .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);
})

Ответ 6

Используйте библиотеку потока управления, например 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)
})