2017-06-02 118 views
1

我很難理解JavaScript承諾。我正在尋找滿足一定條件的對象的Mongoose模型,如果它們存在,我想將該對象變成一個普通的JS對象並添加一個屬性到它。異步findOne()操作forEach循環內

不幸的是,我無法將我的頭圍繞在如何確保我的forEach循環將在我的諾言最終解決之前完全運行。請參閱我的代碼。

// Called to check whether a user has participated in a given list of challenges 
participationSchema.statics.getParticipation = function(user, challenges) { 
    return new Promise((resolve, reject) => { 
    challengesArray = []; 
    challenges.forEach((challenge) => { 
     // Model#findOne() is Async--how to ensure all these complete before promise is resolved? 
     Participation.findOne({user, challenge}) 
     .then((res) => { 
     if (res) { 
      var leanObj = challenge.toObject(); 
      leanObj.participation = true; 
      challengesArray.push(leanObj); 
     } 
     }) 
     .catch(e => reject(e)); 
    }) 
    console.log("CHALLENGES ARRAY", challengesArray); // Challenges Array empty :(
    resolve(challengesArray); 
    }); 
} 

我已經看過類似的問題,但無法得到答案。感謝幫助。

+2

避免['Promise'構造反模式](https://stackoverflow.com/q/23803743/1048572?What-is-the-promise-construction-antipattern-and-how-to-avoid-它),並且不要使用'forEach'!你正在尋找'Promise.all' – Bergi

+0

好吧,由於異步代碼是異步的,你可以在異步結果可用之前清楚地解決'challengesArray' –

回答

1

所以,當你調用getParticipation正在發生的事情是,forEach魯普運行所有的方式,爲Participation.findOne所有個體的承諾都創建,但尚未解決。執行不會等待它們解決並在forEach之後繼續,解決當時仍然爲空的頂級承諾challengesArray。過了一段時間,在forEach創建的承諾開始解決,但他們的結果現在丟失了。另外,正如Bergi在評論中提到的,嵌套承諾被認爲是anti-pattern;承諾應該被鏈接,而不是嵌套。

你想要的是使用類似Promise.all的東西來等待所有的承諾先完成,然後過濾掉所有不存在的結果,最後返回數組。

participationSchema.statics.getParticipation = function(user, challenges) { 
    return Promise.all(challenges.map(challenge => { 
    return Participation.findOne({user, challenge}).then(result => { 
     if (result) { 
     var leanObj = challenge.toObject(); 
     leanObj.participation = true; 
     return leanObj; 
     } 
    }); 
    }) 
    // at this point, results contains an array of `leanObject` and `undefined` depending if the `findOne` call returned anything and the code withing the `if` above was run 
    .then((results) => { 
    return results.filter(result => !!result) // filter out `undefined` results so we only end up with lean objects 
    }); 
} 
+0

啊,這太有意思了!所以在這個設計中,你準備了一個數組中的所有promise,並且'promises.all()'確保它們全部完成,正確嗎?請讓我快速嘗試一下,然後回覆你。 –

+1

不知道你準備什麼。現在發生的事情是,首先,挑戰陣列被轉換爲一系列的promise,代表對Participation.findOne的調用,並且所有的請求都基本上是並行的。然後這些請求開始以某種任意順序完成,並且承諾開始相應地解析,爲每個請求運行'then'子句並檢查結果是否存在,如果結果存在,則將它們轉換爲'leanObj'。一旦所有這些承諾都解決了,'Promise.all'就可以解決從每個承諾中獲得的結果。 – nem035

+0

啊,是的,這就是我所說的準備,就像創造一系列的承諾一樣。也可以在***結果中包含一個'leanObject'和'undefined'的數組,這取決於如果'findOne'調用返回任何內容並且上面的代碼使用'if'被運行*** –