(轉自:http://bbs.tianya.cn/post-itinfo-280080-1.shtml) html
Node.js 的異步機制由事件和回調函數實現,一開始接觸可能會感受違反常規,但習慣 之後就會發現仍是很簡單的。然而這之中其實暗藏了很多陷阱,一個很容易遇到的問題就是 循環中的回調函數,初學者常常容易陷入這個圈套。讓咱們從一個例子開始說明這個問題。 編程
這段代碼的功能很直觀,就是依次讀取文件 a.txt、b.txt 、c.txt ,並輸出文件名和內容。假設這三個文件的內容分別是 AAA 、BBB 和 CCC,那麼咱們指望的輸出結果就是: 數組
a.txt: AAA 閉包
b.txt: BBB 異步
c.txt: CCC 函數式編程
但是咱們運行這段代碼的結果是怎樣的呢?居然是這樣的結果: 函數
undefined: AAA oop
undefined: BBB post
undefined: CCC ui
這個結果說明文件內容正確輸出了,而文件名卻不對,也就意味着,contents 的結果是正確的,但 files[i] 的值是 undefined。這怎麼可能呢,文件名不正確卻能讀取文件內容?既然難以直觀地理解,咱們就把 files[i] 分解並打印出來看看,在讀取文件的回調函數中分別輸出 files、i 和 files[i] 。
運行修改後的代碼,結果以下:
[ 'a.txt', 'b.txt', 'c.txt' ]
3
undefined
[ 'a.txt', 'b.txt', 'c.txt' ]
3
undefined
[ 'a.txt', 'b.txt', 'c.txt' ]
3
undefined
看到這裏是否是有點啓發了呢?三次輸出的 i 的值都是 3 ,超出了 files 數組的下標範圍,所以 files[i] 的值就是 undefined 了。這種狀況一般會在 for 循環結束時發生,例如 for (var i = 0; i < files.length; i++),退出循環時 i 的值就files.length 的值。既然 i 的值是 3 ,那麼說明了事實上 fs.readFile 的回調函數中訪問到的 i 值都是循環退出之後的,所以不能分辨。而 files[i] 做爲 fs.readFile 的第一個參數在循環中就傳遞了,因此文件能夠被定位到,並且能夠顯示出文件的內容。
如今問題就明朗了:緣由是3 次讀取文件的回調函數事實上是同一個實例,其中引用到的 i 值是上面循環執行結束後的值,所以不能分辨。如何解決這個問題呢?咱們能夠利用
JavaScript 函數式編程的特性,手動創建一個閉包:
//forloopclosure.js
上面代碼在 for 循環體中創建了一個匿名函數,將循環迭代變量 i 做爲函數的參數傳遞並調用。因爲運行時閉包的存在,該匿名函數中定義的變量(包括參數表)在它內部的函數(fs.readFile 的回調函數)執行完畢以前都不會釋放,所以咱們在其中訪問到的 i 就分別是不一樣的閉包實例,這個實例是在循環體執行的過程當中建立的,保留了不一樣的值。
補充:閉包的寫法,沒法保證按數組存放文件順序讀取文件內容,至關多個文件讀取操做並行進行,根據文件大小決定讀取的快慢;而forEach是能夠的保證順序讀取;
事實上以上這種寫法並不常見,由於它下降了程序的可讀性,故不推薦使用。大多數狀況下咱們能夠用數組的 forEach 方法解決這個問題:
//callbackforeach.js