2017-10-20 13 views
1

Jouant avec JS moderne et s'est coincé un peu avec ce qui suit.Besoin de conseils sur Promises.all imbriqué

Envisagez d'avoir un système ExtSystem accessible via une API HTTP et une instance Mongo locale. Ils contiennent tous deux des objets avec name et id.

Pour Mongo J'utilise mongoose avec le modèle ObjectSchema ({_id, sourceId, name, internalParam}) où sourceId égal à id de ExtSystem et internalParam existe seulement dans mon application. Pour ExtSystem il y a 2 méthodes de retour request.js Promise:

  • ExtSystem.all retourne un tableau de ids [id, id, id]
  • se {id, name}
  • objet retourne ExtSystem.get

Il y a aussi une fonction globale errHandler qui peut gérer erreurs à la fois request.js et mongoose promesses. Le but à atteindre est de synchroniser Mongo avec ExtSystem: mettre à jour tous les objets de ExtSystem dans Mongo et supprimer ceux présents dans ExtSystem de Mongo.

Ce que je suis venu avec:

ExtSystem.all().then(body => { 
    let basket = []; // will store all promises for both ExtSystem and Mongo requests 
    basket.push(...body.map(o => ExtSystem.get(o.id)); 
    basket.push(ObjectSchema.find({}, 'sourceId')); 

    Promise.all(basket).then(basketDoc => { 
     let mongoObjects = {}, extObjects = {}; 
     basketDoc.pop().forEach(o => mongoObjects[o.sourceId] = o._id); // Mongo retuns array of {_id, sourceId } objects 
     basketDoc.forEach(o => { // ExtSystem returns array of {id, name} objects 
      extObjects[o.id] = { 
       sourceId: o.id, 
       name: o.name 
      } 
     }); 

     let esSet = new Set(Object.keys(extObjects)); 
     let mongoDeleteIds = Object.keys(mongoObjects).filter(oId => !esSet.has(oId)); // Set.has is faster than Array.indexOf 

     let syncPromises = []; 
     syncPromises.push(...Object.keys(extObjects).map(oId => ObjectSchema.findOneAndUpdate({ sourceId: extObjects[oId].sourceId }, extObjects[oId], { upsert: true, new: true }))); 
     syncPromises.push(...mongoDeleteIds.map(oId => ObjectSchema.remove({_id: oId}))); 

     Promise.all(syncPromises).then(_ => { // I don't need results, only the moment when sync is complete 
      ObjectSchema.find().then(doc => { // return actual objects from Mongo 
       someBusinessLogic(doc); 
      }).catch(errHandler); 
     }).catch(errHandler); 
    }).catch(errHandler); 
}).catch(errHandler); 

donc j'ai encore 4 emboîtées Promise Résout et manquant probablement quelque chose. Existe-t-il un meilleur moyen d'y parvenir avec un code moins complexe?

Répondre

3

Les promesses sont conçus pour se débarrasser des pyramides de malheur. Si vous avez imbriqué des promesses, alors vous le faites mal. Les promesses vous permettent de renvoyer une autre promesse dans l'appel afin de les enchaîner. Ainsi, au lieu de faire:

p1.then(stuff => { 
    p2.then(stuff =>{ 
     ... 
    }); 
}); 

Vous devriez faire

p1 
.then(stuff => { 
    return p2; 
}).then(stuff => { 
    return; 
}); 

Si vous avez des variables dont vous avez besoin pour accéder à l'avenir des promesses, vous pouvez les inclure comme une autre promesse, ou this piece of code I créé il y a quelque temps que créer une promesse qui contient un objet réutilisable global.

Promise 
.resolve({})   // creates global object for holding values 
.then(obj => { 
    return pack(obj, taskPromiseA, "a", taskPromiseB, "b"); 
}) 
.then(obj => {  // you can access results from A and B here 
    return pack(obj, taskPromiseC, "c"); 
}) 
.then(console.log); // you can access them all here 
1

Vous pouvez renvoyer une promesse à partir d'un puisable pour l'enchaîner. Parce qu'il peut enchaîner, cela signifie que toutes vos erreurs peuvent être propagées à un gestionnaire.

Votre code pourrait devenir essentiellement:

ExtSystem.all().then(body => { 
    let basket = []; // will store all promises for both ExtSystem and Mongo requests 
    basket.push(...body.map(o => ExtSystem.get(o.id)); 
    basket.push(ObjectSchema.find({}, 'sourceId')); 
    return Promise.all(basket); 
}).then(basketDoc => { 
    let mongoObjects = {}, extObjects = {}; 
    basketDoc.pop().forEach(o => mongoObjects[o.sourceId] = o._id); // Mongo retuns array of {_id, sourceId } objects 
    basketDoc.forEach(o => { // ExtSystem returns array of {id, name} objects 
     extObjects[o.id] = { 
      sourceId: o.id, 
      name: o.name 
     } 
    }); 

    let esSet = new Set(Object.keys(extObjects)); 
    let mongoDeleteIds = Object.keys(mongoObjects).filter(oId => !esSet.has(oId)); // Set.has is faster than Array.indexOf 

    let syncPromises = []; 
    syncPromises.push(...Object.keys(extObjects).map(oId => ObjectSchema.findOneAndUpdate({ sourceId: extObjects[oId].sourceId }, extObjects[oId], { upsert: true, new: true }))); 
    syncPromises.push(...mongoDeleteIds.map(oId => ObjectSchema.remove({_id: oId}))); 
    return Promise.all(syncPromises); 
}).then(_ => { 
    return ObjectSchema.find(); 
}).then(doc => { 
    return someBusinessLogic(doc); 
}).catch(errHandler);