2017-06-02 6 views
1

JavaScriptの約束を理解するのに苦労しています。私は特定の条件を満たすオブジェクトのMongooseモデルを検索しています。存在する場合は、そのオブジェクトをプレーンなJSオブジェクトにしてプロパティを追加したいと考えています。forEachループ内での非同期findOne()操作

残念ながら、私の約束が解決し終わる前に私の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

非同期コードが非同期であるため、非同期結果が利用可能になる前に' challengArray'を明確に解決しています。 –

答えて

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

ああ、これは面白いです!したがって、この設計では、配列内のすべての約束事を準備し、 'promises.all()'はそれらがすべて完全で正しいことを保証しますか?私はこれを本当に素早く試してみましょう。 –

+1

準備が何を意味するか分かりません。起こっているのは、まず、課題の配列が、「Participation.findOne」への呼び出しを表す一連の約束事に変換され、すべての要求が基本的に並行して行われることです。これらの要求は、任意の順序で完了し始め、約束はそれに応じて解決を開始し、それぞれのために 'then'節を実行し、結果が存在するかどうかを調べ、' leanObj'に変換します。これらの約束がすべて解決されると、「約束」は各約束から得られた結果の配列で解決されます。 – nem035

+0

ああ、そうです、それは私が準備によって意味するものです。また、***結果に 'leanObject'と' undefined'の配列があります。これは、 'findOne'呼び出しが何かを返し、' if'を実行したコードが実行されたかどうかによって異なります。*** –

関連する問題