2011-08-19 111 views
4

這幾天我正在用NodeJS構建我的第一個項目,但是我對某個任務有點困惑,我認爲這是一個簡單的任務,我想問題是我對這些知識缺乏瞭解異步方法,但我無法在任何地方找到答案。在NodeJS中使用異步響應的異步調用

我有一個簡單的循環遍歷數組和任何元素,根據一些規則,我會調用一個函數或另一個。 現在某些操作會比其他操作更快,所以我最終可能會返回元素N上的函數,而不是元素N-1上的函數。 爲簡單起見像這樣

for (var i = 0 ; i < 10 ; i++) { 
     if (i%2 === 0) { 
      setTimeout(function(i) { 
       console.log(i); 
      }, 2000); 
     } 
     else { console.log(i); } 
} 

所以任何偶數將印有2秒滯後而奇數數字將被立即打印。 反正運行它,我得到

1 
3 
5 
7 
9 

<<2 seconds break>> 

undefined 
undefined 
undefined 
undefined 
undefined 

看起來甚至值爲「丟失」。 如何傳遞值以確保函數不會丟失輸入值? 我錯過了什麼嗎?

感謝, 莫羅

回答

0
setTimeout(function(i) { 
      console.log(i); 
     }, 2000); 

功能從setTimeout的叫不會與任何參數來調用。 您需要創建一個閉包。

檢出this question和接受的答案。

(這不是一個特定的node.js-問題)

12

setTimeout參數函數被聲明爲取一個參數,i。當setTimeout像沒有參數一樣調用該函數時,參數因此被設置爲undefined

這似乎是要稍好一些,因爲它不再陰影外i變量原先嚐試引用...

setTimeout(function() { 
    console.log(i); 
}, 2000) 

...但如果你運行它,你會發現它打印10 5次,因爲您創建的每個函數都引用相同的i變量,當循環退出條件變爲true並且它終止時,其值將爲10

創建封閉持有的i的價值,因爲它在每個setTimout參數函數創建將這樣的伎倆在循環過程:

setTimeout((function(i) { 
    return function() { 
    console.log(i); 
    } 
})(i), 2000) 

重命名參數傳遞給Immediately-Invoked Function Expression我們只是用可能有助於讓事情更清晰:

setTimeout((function(loopIndex) { 
    return function() { 
    console.log(loopIndex); 
    } 
})(i), 2000) 

我們:

  • 創建一個只接受一個參數並返回另一個函數的函數。
  • 立即調用「外部」函數,將當前值i(它不是一個對象,因此有效地傳遞值)傳遞給它。
  • 「外部」函數返回「內部」函數,該函數作爲參數傳遞給setTimeout

這樣做是因爲:

  1. 創建一個JavaScript函數創建一個新範圍持有函數的參數變量和使用var關鍵字它內聲明的任何其他變量。可以把它看作一個不可見的對象,其屬性對應於變量名稱。

  2. 所有功能都對其範圍進行了定義,作爲其範圍鏈的一部分。即使它不再處於「活動狀態」(例如,爲返回創建的功能),它們仍然可以訪問此範圍。 「內部」函數創建於一個範圍內,該範圍包含一個loopIndex變量,在調用IIFE時設置爲值i。因此,當您嘗試從內部函數中引用名爲loopIndex的變量時,它首先檢查其自己的作用域(並且在那裏找不到loopIndex然後開始向上遍歷其作用域鏈 - 首先,它檢查其中的作用域它被定義,其中確實包含loopIndex變量,其值被傳遞給console.log()

這就是一個閉包 - 一個函數可以訪問定義的範圍,即使範圍的創建函數已經完成執行。

1

儘管您通常使用bind來確保回調函數內的this的值是您希望的值,但您也可以使用它來修改傳遞給綁定函數的參數。在你的情況下,代碼可能看起來是這樣的(我改變了一些變量的名稱,以便它更清楚這是怎麼回事):

for (var i = 0 ; i < 10 ; i++) { 
    if (i % 2 === 0) { 
    setTimeout(function(num) { 
     console.log(num); 
    }.bind(this, i), 2000); 
    } else { 
    console.log(i); 
    } 
} 

在這種情況下,該功能.bind(this, i)重視它。 this只是爲了填充; bind的第一個參數始終是你想要的this解析爲裏面的的綁定函數。然後,我們可以傳入想要添加到綁定函數參數中的任何值 - 在這種情況下,我們希望將當前值i(綁定函數時)傳遞給函數。

您可以通過MDN Docs瞭解有關bind的更多信息。