web-dev-qa-db-ja.com

Lodash:_。forEach with function

私はlodash forEach メソッドをmongoデータベースを呼び出すネストされた関数で使用しようとしています。

var jobs = [];
_.forEach(ids, function(id) {
    JobRequest.findByJobId(id, function(err, result) {
        if(err) callback(err);
        jobs.Push(result);
    });
});

callback(null, jobs);

内部関数が呼び出される前にforEachとコールバックが実行されるため、問題が発生しています。どうすればこれを解決できますか?

Foreachおよびinner関数が完了した後にコールバックを呼び出す必要があります。

12

もう1つのアプローチは、すべてをpromiseにラップすることです。この場合、ジョブの結果は正しい順序で配列にプッシュされます。

var promises = ids.map(function(id) {
    return new Promise(function(resolve, reject) {
        JobRequest.findByJobId(id, function (err, result) {
            if (err) reject(err);
            resolve(result);
        });
    });
});

Promise.all(promises).then(function(jobs) {
    callback(null, jobs);
}, callback);

// or shorter: Promise.all(promises).then(callback.bind(null, null), callback);

JobRequest.findByJobIdリクエストが失敗した場合の潜在的な状況にも対処する必要があることに注意してください。これは非常に簡単です。つまり、エラーコールバックとしてcallbackPromise.allに渡すだけです。

12
dfsq

JobRequest.findByJobIdは非同期操作です。 JavaScriptでは非同期操作をブロックできないため、カウントして手動で同期する必要があります。例(簡潔にするためにエラー処理は省略):

var results = [];
var pendingJobCount = ids.length;

_.forEach(ids, function(id) {
    JobRequest.findByJobId(id, function(err, result) {
        results.Push(result);
        if (--pendingJobCount === 0) callback(null, results);
    });
});

もちろん、このようなことを行うためのラッパー構造もありますが、実際にどのように機能するかを説明したいと思います。これらのラッパーの1つであるpromiseの詳細については、 dfsqの回答 を確認してください。

また、非同期操作が順不同で完了する場合があることにも注意してください。 results配列の順序は、必ずしもids配列の順序と一致するとは限りません。その情報を接続する必要がある場合は、たとえば、配列ではなくマップに結果を収集するなどして、自分で追跡する必要があります。

var results = {};
var pendingJobCount = ids.length;

_.forEach(ids, function(id) {
    JobRequest.findByJobId(id, function(err, result) {
        results[id] = result;
        if (--pendingJobCount === 0) callback(null, results);
    });
});

この例では、ids配列に重複がないことを前提としています。重複キーの結果は上書きされます。

結果に追加情報を挿入することで、エラー処理も同様に機能します。もう一つの例:

results.Push({id: id, error: null, value: result});
8
jwueller