需要有关嵌套Promises.all的建议

时间:2017-10-20 08:09:52

标签: javascript node.js es6-promise

玩现代JS,并在以下方面陷入困境。

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

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

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

还有一个全局函数errHandler可以处理来自request.jsmongoose 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解决方案,可能会遗漏一些东西。有没有一种最好的方法来实现这一点,而不是复杂的代码?

2 个答案:

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