2017-10-20 178 views
1

玩弄现代JS,并陷入了一些与以下几点。需要关于嵌套Promises.all的建议

考虑通过一些HTTP API和本地Mongo实例来访问ExtSystem。它们都包含nameid的对象。

对于蒙戈我使用mongooseObjectSchema模型({_id, sourceId, name, internalParam})其中sourceId等于id从ExtSystem和internalParam只存在于我的应用程序。对于ExtSystem有2种方法返回request.js无极:

  • ExtSystem.all返回ID的数组[id, id, id]
  • ExtSystem.get返回对象本身{id, name}

还有一个全局函数errHandler它可以处理来自request.jsmongoose承诺的错误。要实现的目标是将Mongo与ExtSystem同步:在Mongo中更新ExtSystem中的所有对象,并从Mongo中删除不再存在于ExtSystem中的对象。

我想出了:

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

所以我仍然有4套无极解决,可能失去了一些东西。有没有一种最简单的方式来实现这个代码不复杂?

回答

3

承诺旨在摆脱厄运的金字塔。如果你嵌套承诺,那么你做错了。

承诺允许您在通话中返回另一个承诺,以链接它们。因此,而不是这样做的:

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

你应该做

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

如果您有需要在未来的承诺访问某些变量,您可以把它们作为另一种承诺,或使用this piece of code我创造了一段时间,创造一个承诺,包含一个全球可重用的对象。

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

你可以从一个可以返回一个Promise来链接它。因为它可以链接,这意味着所有的错误都可以传播给一个处理程序。

您的代码基本上可以成为:

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