2015-09-03 87 views
11

我想用一個嵌套函數調用mongo數據庫的lodash forEach方法。Lodash:_.forEach與功能

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

callback(null, jobs); 

我遇到了問題,因爲forEach和回調將在內部函數被調用之前運行。我該如何解決這個問題?

我想在每個和內部函數完成之後調用回調函數。

+0

你應該使用'async.map'。 – SLaks

回答

8

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); 
    }); 
}); 

有,當然,包裝結構做這樣的東西,但我更願意解釋它是如何實際工作。查看dfsq's answer瞭解其中一種包裝的更多細節,稱爲承諾。

另請注意,異步操作可能無序完成。 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}); 
11

還有一個辦法是一切包裝成的承諾,在這種情況下,作業結果將被推入以正確的順序排列:

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請求失敗時,承諾很容易:只需將callback作爲錯誤回調傳遞給Promise.all即可。

+1

假設一個現代的JavaScript引擎(或一個好的polyfill),這絕對是一個更可讀的方法來做到這一點! – jwueller