将额外参数传递给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) { } 

但这似乎不可能。 现在不确定。

你需要了解一个闭包是什么。 在javascript中,当你在一个函数内部使用一个在外部上下文(外部函数或全局函数)中定义的变量时,你会在该变量周围创建一个闭包,它会保持变量的实例化,并让函数每次都继续引用它。被调用(以及项目上具有闭包的任何其他函数实例)。

因为原始变量仍然是实例化的,所以如果在代码中的任何位置更改该变量的值,当函数稍后运行时,它将具有当前更改的值, 而不是首次创建函数时的值。

在我们解决关闭工作正确之前,请注意在循环中重复声明title变量不起作用(实际上,您可以将变量视为基本上被提升function的范围中 – 与其他语言不同, JavaScript中的for循环没有作用域,因此变量只为函数声明一次 ,并且不在循环内声明或重新声明)。 在循环外声明变量应该有助于澄清为什么代码无法正常工作。

同样,当回调运行时,因为它们在同一个变量i有一个闭包,当它递增时它们都会受到影响,并且当它们运行时它们都将使用i当前值(这将是你发现的错误,因为回调循环完全创建回调运行。 异步代码(例如JSON调用响应)在所有同步代码完成执行之前不会运行也无法运行 – 因此保证在执行任何回调之前完成循环。

要解决这个问题,你需要一个新的函数来运行它有自己的作用域,这样在循环内部声明的回调中,每个不同的值都有一个新的闭包。 您可以使用单独的函数执行此操作,或者只在callback参数中使用调用的匿名函数。 这是一个例子:

 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函数)本质thisi i变量的转换为单独的thisi变量,每次引入具有其自己范围的新函数。 也许可以说“参数打破了封闭的价值”。

请注意:由于对象是引用类型,因此在不复制对象的情况下,此技术不适用于对象。 仅仅将它们作为参数传递将不会产生事后无法改变的东西。 您可以随意复制街道地址,但这不会创建新房子。 如果你想要一个能够带来不同东西的地址,你必须建造一所新房子。

您可以使用立即函数(一个立即执行的函数)创建一个闭包,它返回另一个函数:

 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); }; })()); } 

如果你可以在some.url修改服务,那么如果不是为some_array每个项目单独发出HTTP请求,那么你只需在一个HTTP请求中传递数组中的每个项目some_array

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

您的arrays将被JSON序列化并发布到服务器。 假设some_array是一个字符串数组,请求将如下所示:

 POST some.url HTTP/1.1 ... {'items':['a','b','c', ... ]} 

然后,您的服务器脚本应该从请求主体反序列化JSON请求,并循环items数组中的每个项目,返回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请求中,您将显着提高性能。 考虑一下,如果每个网络往返至少花费200毫秒,有5个项目,那么您至少要看1秒延迟。 通过立即请求它们,网络延迟保持恒定200ms。 (显然,对于更大的请求,服务器脚本执行和网络传输时间将会发挥作用,但性能仍然比为每个项目发出单独的HTTP请求更好。)

创建N个闭包并每次传入’i’的值,如下所示:

 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)); } 

我认为某些浏览器在同时进行多个异步调用时遇到问题,因此您可以一次创建一个:

 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); 

我和OP有完全相同的问题但是以不同的方式解决了它。 我用jQuery $ .each替换了我的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://sofzh.miximages.com/javascript/json'; 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; }); }); }); }