87

Вопрос по asynchronous, javascript – Как я могу ждать множество асинхронных функций обратного вызова?

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

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

После того, как ВСЕ эти асинхронные вызовы выполнены, я хочу вычислить минимальное значение для всех массивов.

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

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

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

Заранее спасибо.

  • Error: User Rate Limit Exceededwhich browsersError: User Rate Limit ExceededPromise.all()Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit ExceededjQueryError: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceededreturn responses.map(response => { return response.json(); })Error: User Rate Limit Exceededreturn responses.map(response => response.json()).

    от
  • Promises.all()Error: User Rate Limit ExceededPromise.all().

    от
  • Error: User Rate Limit Exceededgithub.com/github/fetchError: User Rate Limit Exceededbabeljs.io

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Error: User Rate Limit Exceeded

    от
  • Да. Я ожидаю асинхронного вызова внешнего API, чтобы он возвратил методы обратного вызова. Да, я понял это, вот почему я прошу помощи здесь: D

    от codersarepeople
  • Вы можете попробовать это:github.com/caolan/async Очень хороший набор асинхронных утилит.

    от Paul Greyson
  • Заметка,while (not all are done) { } не будет работать. Пока вы заняты ожиданием, ни один из ваших обратных вызовов не может быть запущен.

    от cHao
  • В асинхронном режиме вы имеете в виду ожидание завершения запроса Ajax?

    от Peter Aron Zentai
6 ответов
  • 8

    Вы можете подражать так:

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

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

    countDownLatch.count++;
    

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

    countDownLatch.check();
    

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

  • 4

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

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

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

  • 10

    Вы можете использовать jQuery

    Отложенный объект вместе скогда метод.

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

  • 177

    Вы не очень точно относились к своему коду

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

    Manual Counter

    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 Promises

    Добавление к моему ответу в 2014 году. В наши дни обещания часто используются для решения этого типа проблем, так как jQuery's$.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 Standard Promises

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

    Если вам нужно превратить асинхронную операцию без обещаний в операцию, которая возвращает обещание, вы можете & quot; promisify & quot; это так:

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

  • 14

    Регистрация с 2015 года: теперь у нас есть

    родные обещания всамый последний браузер (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);
    });