2011-05-25 63 views
20

我從來沒有必須使用回調函數,所以我可能犯了一個完全愚蠢的錯誤。我想我在這裏有點理解這個問題,但不知道如何解決它。將額外的參數傳遞給jQuery getJSON()成功回調函數

我的代碼(有點簡單)是:

for (var i = 0; i < some_array.length; i++) { 
    var title = some_array[i]; 
    $.getJSON('some.url/' + title, function(data) { 
     do_something_with_data(data, i); 
    } 

現在據我瞭解,這個匿名函數將僅的getJSON()已接收到的數據調用。但是到了這一點,i沒有我需要的價值。或者,就我的觀察而言,它具有循環完成後的最後一個值(不應該超出界限?)。

其結果是,如果數組的大小爲6,do_something_with_data()將與值5

現在我想調用了五次,只是通過i匿名函數

function(data, i) { } 

但這似乎不可能。 i現在未定義。

回答

45

你需要了解什麼是封閉是。在JavaScript中,如果在函數內部使用在外部上下文(外部函數或全局)中定義的變量,則圍繞該變量創建一個閉包,該閉包將變量實例化並讓函數在每次它繼續引用它時被調用(以及任何其他函數實例在物品上都有閉包)。

因爲原來的變量仍然實例化,如果你改變的變量任何地方代碼,值的功能運行時以後就會有電流變化值,值當函數是第一創建。

之前,我們的地址進行關閉工作的權利,請注意,在循環中重複聲明title變量不工作(其實,你能想到的變量作爲基本爲的懸掛function的scope- - 不像其他語言,for JavaScript中的循環沒有範圍,因此變量只聲明一次,並且而不是在循環內聲明或重新聲明。在循環之外聲明變量應該有助於澄清爲什麼你的代碼不能像你期望的那樣工作。

由於,當回調運行,因爲他們有過相同的變量i封閉,他們都當i增量和它們在運行時將全部使用的i當前值的影響(這將是錯誤的正如你發現的那樣,因爲回調在之後運行,該回路已經完全完成創建回調)。異步代碼(例如JSON調用響應)不會運行,直到所有同步代碼完成執行爲止 - 因此在執行任何回調之前,循環將保證完成。

要解決這個問題,你需要一個新的功能來運行具有其自己的範圍,這樣在循環內聲明的回調,有超過每不同值的新閉合。你可以用一個單獨的函數來做到這一點,或者只是在回調參數中使用一個被調用的匿名函數。這裏有一個例子:

var title, i; 
for (i = 0; i < some_array.length; i += 1) { 
    title = some_array[i]; 
    $.getJSON(
     'some.url/' + title, 
     (function(thisi) { 
      return function(data) { 
      do_something_with_data(data, thisi); 
      // Break the closure over `i` via the parameter `thisi`, 
      // which will hold the correct value from *invocation* time. 
      }; 
     }(i)) // calling the function with the current value 
    ); 
} 

爲清楚起見,我會打破它到一個單獨的功能,所以你可以看到發生了什麼事情:

function createCallback(item) { 
    return function(data) { 
     do_something_with_data(data, item); 
     // This reference to the `item` parameter does create a closure on it. 
     // However, its scope means that no caller function can change its value. 
     // Thus, since we don't change `item` anywhere inside `createCallback`, it 
     // will have the value as it was at the time the createCallback function 
     // was invoked. 
    }; 
} 

var title, i, l = some_array.length; 
for (i = 0; i < l; i += 1) { 
    title = some_array[i]; 
    $.getJSON('some.url/' + title, createCallback(i)); 
    // Note how this parameter is not a *reference* to the createCallback function, 
    // but the *value that createCallback() returns*, which is itself a function. 
} 

注:因爲你的陣列顯然只具有職稱的話,您可以考慮使用title變量而不是i,這需要您返回some_array。但無論哪種方式都有效,你知道你想要什麼。考慮這個回調-創建功能(或者匿名一個或createCallback一個)在本質上轉換i可變成單獨thisi變量的,通過每次引入一個新的

一種可能有用的方式函數有自己的範圍。也許可以這樣說,「參數將數值從關閉中分解出來」。

只是要小心:這種技術不會複製它們,因爲對象是引用類型。僅僅將它們作爲參數傳遞將不會產生事後不能改變的東西。你可以複製一個你喜歡的街道地址,但這不會創建一個新房子。如果你想要一個導致不同事物的地址,你必須建造一座新房子。

6

您可以創建使用即時功能關閉(一個執行的時候了),返回另一個功能:

for (var i = 0; i < some_array.length; i++) { 
    var title = some_array[i]; 
    $.getJSON('some.url/' + title, (function() { 
     var ii = i; 
     return function(data) { 
      do_something_with_data(data, ii); 
     }; 
    })()); 
} 
+1

+1除了@Chris想要保留對每個「i」的引用。 – Jeremy 2011-05-25 18:38:32

+0

噢,謝謝,我錯過了。新增了它。 – patorjk 2011-05-25 18:42:59

+0

我試圖去適應這個問題,但是**數據現在變成了什麼值?現在對我來說是不確定的。 – Chris 2011-05-25 18:52:44

1

創建n個瓶蓋,並通過在「我」每一次,像這樣的價值:

var i, title; 
for (i = 0; i < some_array.length; i++) { 
    title = some_array[i]; 
    $.getJSON('some.url/' + title, (function(i_copy) { 
     return function(data) { 
      do_something_with_data(data, i_copy); 
     }; 
    })(i)); 
} 
+0

請注意,jslint會要求您在循環外部移動var聲明,並將匿名函數調用放在括號內,而不是外部。 – ErikE 2011-05-25 18:54:22

0

我覺得有些瀏覽器在使用的同時進行多個異步調用的麻煩,所以你可以讓他們一次一個:

var i; 
function DoOne(data) 
{ 
    if (i >= 0) 
     do_something_with_data(data, i); 
    if (++i >= some_array.length) 
     return; 
    var title = some_array[i]; 
    $.getJSON('some.url/' + title, DoOne); 
} 

// to start the chain: 
i = -1; 
DoOne(null); 
+2

請勿使用'async:false'。它會導致** whole **瀏覽器(包括UI線程)在網絡請求期間鎖定。如果你凍結瀏覽器,你的用戶將會非常不高興。 – josh3736 2011-05-25 19:25:04

+0

Firefox和Chrome只鎖定了當前的選項卡,但指出,我從我的答案中刪除了這一點。 – 2011-05-25 20:29:24

3

如果你能修改在some.url服務,這將是如果不是爲some_array中的每個項目分別發出一個HTTP請求,只需在單個HTTP請求中傳遞數組中的每個項目,那麼會更好。

$.getJSON('some.url', { items: some_array }, callback); 

您的數組將被JSON序列化併發布到服務器。假設some_array是一個字符串數組,該請求將是這樣的:

POST some.url HTTP/1.1 
... 

{'items':['a','b','c', ... ]} 

你的服務器腳本則應該反序列化請求主體和環比items陣列中的每個項目的JSON請求,返回一個JSON序列化響應數組。

HTTP/1.1 200 OK 
... 

{'items':[{id:0, ... }, {id:1, ... }, ... ]} 

(或者是任何數據你回來了。)如果你的反應項目在相同的順序要求的項目,很容易塊的東西重新走到一起。在您的成功回調中,只需將物品索引與some_array的索引匹配即可。全部放在一起:

$.getJSON('some.url', { items: some_array }, function(data) { 
    for (var i = 0; i < data.items.length; i++) { 
     do_something_with_data(data.items[i], i); 
    } 
}); 

通過「配料了」您的要求爲這樣一個HTTP請求,你會顯著提高性能。考慮一下,如果每個網絡的往返時間至少需要200ms,而且有5個項目,那麼你至少要看到1秒的延遲。通過一次請求它們,網絡延遲保持恆定200ms。 (很明顯,隨着請求量的增加,服務器腳本執行和網絡傳輸時間將會發揮作用,但是性能仍然會比如果您爲每個項目發出單獨的HTTP請求更好一個量級)。

+1

這些都是好點。這可能是最好的結合請求。 – ErikE 2011-05-26 16:14:43

0

我剛好與OP相同的問題,但以不同的方式解決問題。我用jQuery $替換了我的JavaScript for'循環'。每次迭代都會調用一個函數,我認爲這個函數可以解決回調'時序'問題。我將外部數據數組組合到一個JavaScript對象中,以便我可以引用傳遞給JSON URL的參數和該對象的同一元素中的其他字段。我的對象元素使用PHP從mySQL數據庫表中出來。

var persons = [ 
{ Location: 'MK6', Bio: 'System administrator' }, 
{ Location: 'LU4', Bio: 'Project officer' }, 
{ Location: 'B37', Bio: 'Renewable energy hardware installer' }, 
{ Location: 'S23', Bio: 'Associate lecturer and first hardware triallist' }, 
{ Location: 'EH12', Bio: 'Associate lecturer with a solar PV installation' } 
]; 

function initMap() { 
    var map = new google.maps.Map(document.getElementById('map_canvas'), { 
    center: startLatLon, 
    minZoom: 5, 
    maxZoom: 11, 
    zoom: 5 
    }); 
    $.each(persons, function(x, person) { 
    $.getJSON('http://maps.googleapis.com/maps/api/geocode/json?address=' + person.Location, null, function (data) { 
     var p = data.results[0].geometry.location; 
     var latlng = new google.maps.LatLng(p.lat, p.lng); 
     var image = 'images/solarenergy.png'; 
     var marker = new google.maps.Marker({ 
     position: latlng, 
     map: map, 
     icon: image, 
     title: person.Bio 
     }); 
     google.maps.event.addListener(marker, "click", function (e) { 
     document.getElementById('info').value = person.Bio; 
     }); 
    }); 
    }); 
} 
相關問題