2013-03-01 94 views
27

回調我做了雙forEach循環像這裏面的一些計算:(JavaScript)的同步foreach循環裏面

array.forEach(function(element){ 
    Object.keys(element).forEach(function(key){ 

     /* some complex computations with asynchronous callbacks */   

    }); 
}); 

someFunctionHere(); 

是有辦法的循環做做someFunctionHere()功能之前首先完成?或任何方式,程序就會知道,如果環路繼續someFunctionHere()前完成......

我可能會丟失一些論壇,但我發現那些沒有幫助我什麼,我想實現,並通過我的方式我在NodeJS中這樣做,我也問是否有現成的庫可以做到這一點。

我忘了添加它或應該是另一個問題?

有沒有辦法同步做迭代,它只會在當前迭代完成後才進行下一次迭代? (對不起,這一點)

感謝您的幫助......

+1

是的,它基本上是100%保證「someFunctionHere」將在異步操作完成之前調用。 – Pointy 2013-03-01 16:15:27

+0

關於編輯:Golo的答案顯示瞭如何使用async.js來做你想做的事情。 (我相信''Clumpy.js'](http://www.tumuski.com/code/clumpy/overview/)也可以用於這個。)我的答案顯示瞭如何「推出自己的」解決方案if ,出於某種原因,你不能(或者根本不想)使用第三方庫。 – 2013-03-01 21:18:24

+0

我已經使用async.forEach()完成了上述問題,但是由於迭代一次不會被稱爲一次迭代(或者如果迭代同步完成,我可能在代碼中出現邏輯錯誤),所以會混淆我的數據庫,所以Golo的答案可能與我在asyn.forEach()中所做的相同,我的意思是來自術語'異步'的迭代不是同步完成的嗎? – megamoth 2013-03-02 00:24:30

回答

44

看看async.js,特別是其control flow語句,例如eachwhilstuntil

使用async.js你可以得到你想要的。

在你的實際情況,你想要什麼each功能(已以前被稱爲forEach),分別爲eachSeries功能不併行運行的單次迭代,但在串行(見documentation for eachSeries瞭解詳細信息)。

爲了提供一個例子:

async.eachSeries([ 2, 3, 5, 7, 11 ], function (prime, callback) { 
    console.log(prime); 
    callback(); // Alternatively: callback(new Error()); 
}, function (err) { 
    if (err) { throw err; } 
    console.log('Well done :-)!'); 
}); 

這將迭代素數的陣列過去後,對方打印他們以正確的順序,一個,最後打印出Well done :-)!

+0

感謝這一個,但使用異步庫不是一個選項,我的意思是循環中的每個迭代應該是真正的同步,因爲它正在做密集的數據庫操作,它應該只是一次迭代,我忘了把這個在問題中也對此表示懷疑 – megamoth 2013-03-01 18:30:01

+0

示例正好顯示了此行爲:循環內的每個迭代都運行同步。它完美的序列化。 – 2013-03-01 20:02:19

+0

在提出問題之前,我在我以前的解決方案中使用了async.forEach(),因此異步中的迭代一次只能執行一次?因爲如果是這樣的話,我可能會在代碼中出現一些邏輯錯誤,但我沒有看到它,或者一次只能執行一次,但在繼續進行下一次迭代之前不會等待當前迭代結束 – megamoth 2013-03-02 00:37:13

6

你可以用在倒計時關閉您的回調:

var len = array.length; 

function countdownWrapper(callback, callbackArgs) { 
    callback(callbackArgs); 
    if (--len == 0) { 
     someFunctionHere(); 
    } 
} 

array.forEach(function(element){ 
    Object.keys(element).forEach(function(key){ 

     var wrappedCallback = countdownWrapper.bind(callback); 
     /* some complex computations with asynchronous WRAPPED callbacks */ 

    }); 
}); 

如果回調有不同數量的參數,你可以做一個小的手術上arguments,而不是使用一個明確的callbackArgs參數。

編輯您的編輯闡明瞭您希望在上次計算完成回調後開始每個複雜計算。這也可以通過關閉輕鬆安排:

function complexOp(key, callback) { /* . . . */ } 

function originalCallback(...) { /* . . . */ } 

function doSomethingElse() { /* . . . */ } 

var iteratorCallback = (function (body, callback, completion) { 
    var i = 0, len = array.length; 
    return function iterator() { 
     callback.apply(null, arguments); 
     if (++i < len) { 
      body(array[i], iterator); 
     } else { 
      completion(); 
     } 
    }; 
}(complexOp, originalCallback, doSomethingElse)); 

// kick everything off: 
complexOp(array[0], iteratorCallback); 
+0

這真的很有幫助它可能解決我的問題之一,我仍然要檢查這一點,謝謝! – megamoth 2013-03-01 18:32:26

+0

這基本上沒有別的,但是async.js給你開箱即用。它只是不「標準化」,而是「專有」。 – 2013-03-01 20:02:59

+0

@GoloRoden - 專有?這是荒謬的。網絡上有很多這種技巧的例子,並且很容易就可以實時處理它。(爲了記錄,我沒有從任何地方複製我帖子中的代碼。)文章的要點是,你不需要像[async.js](https://github.com/caolan/)那樣的整個包異步)或[Clumpy.js](http://www.tumuski.com/code/clumpy/overview/);它只是幾行代碼。 – 2013-03-01 20:21:07

1

高性能的解決方案:

有可能是當你可能想/允許處理數組異步/平行,但想要一個函數後調用的情況下, forEach的所有成員都已處理完畢。 例如:

var async = require('async'); 

async.forEach(array,function(elementOfArray, callback){ 
    //process each element of array in parallel 
    // .. 
    // .. 
    // .. 
    callback(); //notify that this iteration is complete 
}, function(err){ 
if(err){throw err;} 
console.log("processing all elements completed"); 
}); 

因此,這種方式可以執行非阻塞CPU密集型操作。

注意:如果對大型數組使用eachSeries,那麼很多迭代回調可能會使堆棧溢出。

這裏閱讀: https://github.com/caolan/async#control-flow

1

對於支持的承諾(或使用填充工具)瀏覽器/我的NodeJS已經實現我自己的foreach有回調同步的版本,所以我就分享這裏..

回調必須返回一個承諾..

function forEachSync(array, callback) { 
    let lastIndex = array.length - 1; 
    let startIndex = 0; 

    return new Promise((resolve, reject) => { // Finish all 
     let functionToIterateWith = (currIndex) => { 
      if (currIndex > lastIndex) { 
       return resolve(); 
      } else { 
       callback(array[currIndex]).then(() => { 
        functionToIterateWith(currIndex + 1); 
       }).catch(err => reject(err)); 
      } 
     } 

     functionToIterateWith(startIndex); 
    }); 
}