2012-05-27 74 views
2

很多使用節點後,我不得不習慣以非阻塞的方式編寫我的代碼,但是我可以做到這一點的主要方式是使用函數它們本身就是異步的。例如:stat(f,callback)forEach(array, callback)他們會自動採取您所給予的任何回撥,並將其視爲主執行高速公路,並在被調用後立即返回。有沒有辦法使代碼塊本身非阻塞

我想知道的是:我怎麼能告訴ECMA引擎執行的函數異步nomatter它是什麼?

我的特殊用例涉及迭代DOM子列表上的for-loop來解析數千個元素;我的問題是每個其他元素都是我想跳過的文本節點。雖然我會使用forEach()這不是最好的,但我只能看到for(a,i=0;a=table[i];i=i+2){/*process 'a'*/}能夠糾正這種情況,但以阻止爲代價。最好的行動是什麼?

紅利問題: NodeJS的編碼實踐是否在客戶端應用程序中持有任何理由,因爲JS需要進行繁重的工作。

回答

3

注意:Array.prototype.forEach是同步的,而不是異步的。 JS標準(ECMAScript 5th edition)中定義的任何東西都不能是異步的,因爲標準沒有定義異步語義(Node.js和DOM)。

你可以(在瀏覽器和Node.js的中作品)使用setTimeoutprocess.nextTick(Node.js的專用):

for (...) { 
    doWorkAsync(...); 
} 

function doWorkAsync(...) { 
    setTimeout(doWorkSync.bind(null, ...), 0); 
} 

function doWorkSync(...) { 
    ... 
} 

使用自由變量時,如果選擇利用倒閉,作爲要小心最終調用回調時,變量可能會發生變異。

有了一個異步的框架,如Q by kriskowal(跨越的Node.js和現代瀏覽器便攜式),你可以做MapReduce的編程風格:

var Q = require('q'); // npm package 'q' 

function getWorkloads() { 
    var workloads = [ ]; 
    for (...) { 
     workloads.push(Q.fcall(doWorkSync.bind(null, ...))); 
    } 
    return workloads; 
} 

Q.all(getWorkloads()).then(function (results) { 
    // results array corresponds to 
    // the array returned by getWorkloads. 
}); 
+0

感謝您的澄清,謝謝! – TERMtm

+0

雖然快速問題:廣泛使用setTimeout,安全還是昂貴? – TERMtm

+0

如果你正在使用超時時間爲'0'的'setTimeout',你應該改用[process.nextTick'](http://nodejs.org/docs/latest/api/process.html#process_process_nexttick_callback)。 「在事件循環的下一個循環中調用此回調函數,這不是setTimeout(fn,0)的簡單別名,它更有效率。」 –

0

我在同一條船上。我有點喜歡Node的異步函數,所以我寫了這個async For和ForEach函數。它使用「setTimeout(Func,0);」招。

這裏是庫:

var WilkesAsyncBurn = function() 
    { 
    var Now = function() {return (new Date());}; 
    var CreateFutureDate = function(milliseconds) 
    { 
     var t = Now(); 
     t.setTime(t.getTime() + milliseconds); 
     return t; 
    }; 
    var For = function(start, end, eachCallback, finalCallback, msBurnTime) 
    { 
     var i = start; 
     var Each = function() 
     { 
     if(i==-1) {return;} //always does one last each with nothing to do 
     setTimeout(Each,0); 
     var burnTimeout = CreateFutureDate(msBurnTime); 
     while(Now() < burnTimeout) 
     { 
      if(i>=end) {i=-1; finalCallback(); return;} 
      eachCallback(i); 
      i++; 
     } 
     }; 
     Each(); 
    }; 
    var ForEach = function(array, eachCallback, finalCallback, msBurnTime) 
    { 
     var i = 0; 
     var len = array.length; 
     var Each = function() 
     { 
     if(i==-1) {return;} 
     setTimeout(Each,0); 
     var burnTimeout = CreateFutureDate(msBurnTime); 
     while(Now() < burnTimeout) 
     { 
      if(i>=len) {i=-1; finalCallback(array); return;} 
      eachCallback(i, array[i]); 
      i++; 
     } 
     }; 
     Each(); 
    }; 

    var pub = {}; 
    pub.For = For;   //eachCallback(index); finalCallback(); 
    pub.ForEach = ForEach; //eachCallback(index,value); finalCallback(array); 
    WilkesAsyncBurn = pub; 
    }; 

實例應用:

WilkesAsyncBurn(); // Init the library 
    console.log("start"); 
    var FuncEach = function(index) 
    { 
    if(index%10000==0) 
    { 
     console.log("index=" + index); 
    } 
    }; 
    var FuncFinal = function() 
    { 
    console.log("done"); 
    }; 
    WilkesAsyncBurn.For(0,2000000,FuncEach,FuncFinal,50); 

打印: 指數= 10000 指數= 20000 指數= 30000 等 「完成」

更多研究如果感興趣:

setTimeout和setInterval的最小開銷時間約爲2到10毫秒,因此,釋放數千或數百萬個定時器的速度將無限緩慢。所以基本上,如果你需要在不鎖定瀏覽器的情況下執行數千或更多的循環,你需要更像是一個線程(gasp),並將某些代碼「燒」一段時間,而不是設置迭代次數。

+0

感謝您發佈您的答案!你會考慮縮減你的代碼示例,只是爲了解釋手頭的問題嗎?事實上,很難真正「獲得」答案的本質。另外,在代碼註釋之外加入解釋通常是有幫助的,因爲它們可能更難以閱讀(並且人們經常忽略它們) –