web-dev-qa-db-fra.com

Comment exécuter plusieurs fonctions asynchrones puis exécuter le rappel

Dans mon code NodeJS, je dois passer 2 ou 3 appels API et chacun renverra des données. Une fois tous les appels d'API terminés, je souhaite collecter toutes les données dans un seul objet JSON à envoyer au frontend.

Je sais comment faire cela en utilisant les rappels API (le prochain appel se produira dans le rappel de l'appel précédent) mais cela serait lent:

//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

  });

});

Je sais que vous pourriez également faire quelque chose de similaire et de mieux avec des promesses, mais je pense que le même concept s'applique lorsque le prochain appel ne s'exécutera pas avant la fin de l'appel en cours.

Existe-t-il un moyen d'appeler toutes les fonctions en même temps, mais que mon dernier bloc de code attende que tous les appels d'API se terminent et fournissent des données avant de s'exécuter?

27
Coop

Les promesses vous donnent Promise.all() (ceci est vrai pour les promesses natives ainsi que celles de bibliothèque comme bluebird's).

pdate: Puisque Node 8, vous pouvez utiliser util.promisify() comme vous le feriez avec Bluebird's .promisify()

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

Alors, que pouvez-vous faire (natif):

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

Si vous avez Bluebird, c'est encore plus simple:

var requestAsync = Promise.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
});
48
Madara Uchiha

Des sons comme async.parallel () feraient également l'affaire si vous souhaitez utiliser 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);
});
12
Ben

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

Promise.all est désormais inclus avec ES6, vous n'avez donc plus besoin de bibliothèques tierces.

"Promise.all attend tous les accomplissements (ou le premier rejet)"

J'ai configuré un Gist pour démontrer Promise.all () avec des itérations de refactorisation à: https://Gist.github.com/rainabba/21bf3b741c6f9857d741b69ba8ad78b1

J'utilise un IIFE (expression de fonction immédiatement impliquée). Si vous n'êtes pas familier, vous voudrez être pour l'exemple ci-dessous bien que le Gist montre comment utiliser un 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 } ) } )
1
rainabba