2017-10-20 150 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);