需要关于嵌套Promises.all的build议

玩现代的JS,并与以下几点卡住了。

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

对于Mongo,我在ObjectSchema模型( {_id, sourceId, name, internalParam} )中使用sourceId {_id, sourceId, name, internalParam}其中sourceId等于来自ExtSystem的id ,而internalParam只存在于我的应用程序中。 ExtSystem有两个方法返回request.js Promise:

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

还有一个全局函数errHandler可以处理来自request.jserrHandler Promises的错误。 要实现的目标是将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个嵌套Promise解决,可能会丢失一些东西。 有没有一个最好的方式来实现这个不太复杂的代码?

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

承诺允许您在通话中返回另一个承诺,以链接它们。 所以不要这样做:

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

你应该做

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

如果你在将来的承诺中需要访问一些variables,你可以将它们作为另一个承诺,或者使用我刚才创build的这段代码创build一个包含全局可重用对象的承诺。

 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 

你可以从一个可以返回一个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);