玩现代JS,并在以下方面陷入困境。
考虑通过一些HTTP API和本地Mongo实例访问ExtSystem。它们都包含name
和id
的对象。
对于Mongo,我使用mongoose
与ObjectSchema
模型({_id, sourceId, name, internalParam}
),其中sourceId
等于id
来自ExtSystem和internalParam
}仅存在于我的应用中。对于ExtSystem,有两种方法返回request.js
Promise:
[id, id, id]
{id, name}
还有一个全局函数errHandler
可以处理来自request.js
和mongoose
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解决方案,可能会遗漏一些东西。有没有一种最好的方法来实现这一点,而不是复杂的代码?
答案 0 :(得分:3)
Promise旨在摆脱厄运金字塔。如果你有嵌套的Promises,那么你做错了。
Promises允许您在调用中返回另一个promise以链接它们。所以不要这样做:
p1.then(stuff => {
p2.then(stuff =>{
...
});
});
你应该做
p1
.then(stuff => {
return p2;
}).then(stuff => {
return;
});
如果您在将来的promises中有一些需要访问的变量,可以将它们包含在另一个promise中,或者使用我之前创建的this piece of code创建包含全局可重用对象的promise。
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 :(得分: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);