J'ai une structure de type tableau qui expose les méthodes asynchrones. La méthode async appelle des structures de tableau de retour qui à leur tour exposent davantage de méthodes asynchrones. Je crée un autre objet JSON pour stocker les valeurs obtenues à partir de cette structure et je dois donc faire attention à garder une trace des références dans les rappels.

J'ai codé une solution de force brute, mais je voudrais apprendre une solution plus idiomatique ou plus propre.

  1. Le motif doit être reproductible pour n niveaux d'imbrication.
  2. J'ai besoin d'utiliser promise.all ou une technique similaire pour déterminer quand résoudre la routine de fermeture.
  3. Tous les éléments n'impliqueront pas nécessairement un appel asynchrone. Donc, dans une promesse imbriquée, je ne peux pas simplement faire des affectations à mes éléments de tableau JSON en fonction de l'index. Néanmoins, j'ai besoin d'utiliser quelque chose comme promise.all dans forEach imbriqué pour m'assurer que toutes les affectations de propriété ont été effectuées avant de résoudre la routine de fermeture.
  4. J'utilise la librairie Bluebird promise, mais ce n'est pas une exigence

Voici un code partiel -

var jsonItems = [];

items.forEach(function(item){

  var jsonItem = {};
  jsonItem.name = item.name;
  item.getThings().then(function(things){
  // or Promise.all(allItemGetThingCalls, function(things){

    things.forEach(function(thing, index){

      jsonItems[index].thingName = thing.name;
      if(thing.type === 'file'){

        thing.getFile().then(function(file){ //or promise.all?

          jsonItems[index].filesize = file.getSize();
145
user3205931 14 juil. 2015 à 20:46

2 réponses

J'ai vécu la même situation. J'ai résolu en utilisant deux Promise.All ().

Je pense que c'était vraiment une bonne solution, alors je l'ai publiée sur npm: https: //www.npmjs. com / package / promise-foreach

Je pense que votre code sera quelque chose comme ça

var promiseForeach = require('promise-foreach')
var jsonItems = [];
promiseForeach.each(jsonItems,
    [function (jsonItems){
        return new Promise(function(resolve, reject){
            if(jsonItems.type === 'file'){
                jsonItems.getFile().then(function(file){ //or promise.all?
                    resolve(file.getSize())
                })
            }
        })
    }],
    function (result, current) {
        return {
            type: current.type,
            size: jsonItems.result[0]
        }
    },
    function (err, newList) {
        if (err) {
            console.error(err)
            return;
        }
        console.log('new jsonItems : ', newList)
    })
1
saulsluz 23 juin 2017 à 22:18

Voici un exemple simple utilisant réduire. Il s'exécute en série, maintient l'ordre d'insertion et ne nécessite pas Bluebird.

/**
 * 
 * @param items An array of items.
 * @param fn A function that accepts an item from the array and returns a promise.
 * @returns {Promise}
 */
function forEachPromise(items, fn) {
    return items.reduce(function (promise, item) {
        return promise.then(function () {
            return fn(item);
        });
    }, Promise.resolve());
}

Et utilisez-le comme ceci:

var items = ['a', 'b', 'c'];

function logItem(item) {
    return new Promise((resolve, reject) => {
        process.nextTick(() => {
            console.log(item);
            resolve();
        })
    });
}

forEachPromise(items, logItem).then(() => {
    console.log('done');
});

Nous avons trouvé utile d'envoyer un contexte optionnel en boucle. Le contexte est facultatif et partagé par toutes les itérations.

function forEachPromise(items, fn, context) {
    return items.reduce(function (promise, item) {
        return promise.then(function () {
            return fn(item, context);
        });
    }, Promise.resolve());
}

Votre fonction de promesse ressemblerait à ceci:

function logItem(item, context) {
    return new Promise((resolve, reject) => {
        process.nextTick(() => {
            console.log(item);
            context.itemCount++;
            resolve();
        })
    });
}
40
Steven Spungin 12 mars 2017 à 17:14