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

Как запустить несколько асинхронных функций, а затем выполнить обратный вызов

В моем коде NodeJS мне нужно сделать 2 или 3 вызова API, и каждый из них вернет некоторые данные. После завершения всех вызовов API я хочу собрать все данные в один объект JSON для отправки во внешний интерфейс.

Я знаю, как это сделать, используя обратные вызовы API (следующий вызов произойдет в предыдущем обратном вызове), но это будет медленным:

//1st request
request('http://www.example.com', function (err1, res1, body) {

  //2nd request
  request('http://www.example2.com', function (err2, res2, body2) {

    //combine data and do something with it

  });

});

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

Есть ли способ вызова всех функций одновременно, но для моего последнего блока кода ждать завершения всех вызовов API и подачи данных перед выполнением?

4b9b3361

Ответ 1

Promises дает вам Promise.all() (это верно для native promises, а также для библиотек, таких как bluebird).

Итак, что вы можете сделать (native):

function requestAsync(url) {
    return new Promise(function(resolve, reject) {
        request(url, function(err, res, body) {
            if (err) { return reject(err); }
            return resolve([res, body]);
        });
    });
}
Promise.all([requestAsync('url1'), requestAsync('url2')])
    .then(function(allData) {
        // All data available here in the order it was called.
    });

Если у вас есть синяя птица, это еще проще:

var requestAsync = promisify(request);
var urls = ['url1', 'url2'];
Promise.all(urls.map(requestAsync)).then(allData => {
    // All data available here in the order of the elements in the array
});

Ответ 2

Похоже, что async.parallel() также выполнит задание, если вы хотите использовать async:

var async = require('async');

async.parallel({
    one: function(parallelCb) {
        request('http://www.example1.com', function (err, res, body) {
            parallelCb(null, {err: err, res: res, body: body});
        });
    },
    two: function(parallelCb) {
        request('http://www.example2.com', function (err, res, body) {
            parallelCb(null, {err: err, res: res, body: body});
        });
    },
    three: function(parallelCb) {
        request('http://www.example3.com', function (err, res, body) {
            parallelCb(null, {err: err, res: res, body: body});
        });
    }
}, function(err, results) {
    // results will have the results of all 3
    console.log(results.one);
    console.log(results.two);
    console.log(results.three);
});

Ответ 3

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

Promise.all теперь включен в ES6, поэтому вам вообще не нужны никакие сторонние библиотеки.

"Promise.all ждет всех исполнений (или первого отклонения)"

Я настроил gist, чтобы продемонстрировать Promise.all() с рефакторингом итераций по адресу: https://gist.github.com/rainabba/21bf3b741c6f9857d741b69ba8ad78b1

Я использую IIFE (сразу же задействованное выражение функции). Если вы не знакомы, вы захотите быть примером ниже, хотя суть показывает, как с использованием IIFE. https://en.wikipedia.org/wiki/Immediately-invoked_function_expression

TL; DR

( function( promises ){
    return new Promise( ( resolve, reject ) => {
        Promise.all( promises )
            .then( values => {
                console.log("resolved all promises")
                console.dir( values );
                resolve( values.reduce( (sum,value) => { return sum+value }) ); //Use Array.prototype.reduce() to sum the values in the array
            })
            .catch( err => {
                console.dir( err );
                throw err;
            });

    });
})([ 
    new Promise( ( resolve, reject ) => {
        console.log("resolving 1");
        resolve( 1 );
    }),
    new Promise( ( resolve, reject ) => {
        console.log("resolving 2");
        resolve( 2 );
    })
 ]).then( sum => { console.dir( { sum: sum } ) } )