如何在循环调用时显示 JS 异步结果?
How to display a JS asynchronous result when called on a loop?
我正在做一个个人项目,我被困在似乎是 "basic" 同步/异步 Javascript 的问题上。
总而言之,我正在调用异步 API,然后在屏幕上显示结果。
格式化的期望结果是:"At X kilometers from Y",其中 X 是从 API 计算并正确返回的,Y 是地名(未正确显示)。
这是我的代码,为了更好地理解,有一些注释:
//loadedLandmarks.length is **always** between 1 and 3. No exception.
//In this sample, let's say we have 3 items in loadedLandmarks
for(var i = 0 ; i < loadedLandmarks.length ; i++)
{
var currentLandmarkName = loadedLandmarks[i].customInfo.Name;
landmarksName.push(loadedLandmarks[i].customInfo.Name);
//landmarkName contains the good names, for example : "My school", "My home", "My favorite nightclub" (here after 3 pass on the loop)
console.log(landmarksName);
//DisplayDistanceFromLandmarks is my method which calls the asynchronous API. It seems OK.
DisplayDistanceFromLandmarks(pos, i).then(function(response) {
//The response variable contains correct informations from the API
var origins = response.originAddresses;
var destinations = response.destinationAddresses;
var results = response.rows[0].elements;
//I explain this line below
console.log(loadedLandmarks)
//Then I'm formatting the result to display it on screen (I only paste here the interesting part)
distances += "<br />At " + results[0].distance.value + "kms from" + currentLandmarkName;
return distances;
}).done( /*some other things*/ );
显示的结果是:
At 5 kms from [insert here the LAST currentLandmarkName]
At 8.5 kms from [insert here the LAST currentLandmarkName]
At 0.2 kms from [insert here the LAST currentLandmarkName]
而应该是:
At 5 kms from [insert here the FIRST currentLandmarkName]
At 8.5 kms from [insert here the SECOND currentLandmarkName]
At 0.2 kms from [insert here the THIRD currentLandmarkName]
我不明白的是,当我写console.log(loadedLandmarks)
时,数组的内容是正确的,loadedLandmarks[0].Name
=名字,loadedLandmarks[1].Name
=第二个名字等等
但是,i
始终等于 3,而 currentLandmarkName
始终等于最后一个地标名称。
好像被覆盖了,我不明白为什么。
我是 JS 和异步问题的新手,有人可以解释我为什么会遇到这种行为,而且非常重要的是,如何纠正它?
此方法 DisplayDistanceFromLandmarks
是异步的,这意味着它不会阻止其余代码的执行。所以当你像这样在回调中形成 html 字符串 distances
时:
distances += "<br />At " + results[0].distance.value + "kms from" + currentLandmarkName;
for
循环已经结束,回调函数调用已排队,稍后 return 它们的值。回调函数在其范围内没有 currentLandmarkName
,因为它定义为
var currentLandmarkName = loadedLandmarks[i].customInfo.Name;
我觉得你可以做两件事:
- 将变量
currentLandmarkName
传递给 DisplayDistanceFromLandmarks
方法,从而将其引入作用域。
- 您也可以尝试使用 while 循环并增加计数器,即仅在回调内增加变量
i
。这样你就强制异步行为是同步的。因此,循环的下一次迭代只会在解决承诺并评估回调后发生。
我看到了一篇非常好的文章,它解释了 javascript 的异步行为和闭包的概念。你可能想读一读 http://www.javascriptissexy.com/understand-javascript-closures-with-ease/
希望它能让您朝着正确的方向开始。干杯!
嗨,
这是 JavaScript 和闭包的典型问题。
在传递给 DisplayDistanceFromLandmarks(pos, i).then()
的函数中,您使用了变量 currentLandmarkName
,这给您带来了一些麻烦。
这个变量不是在该函数内部定义的,而是在 for
循环中定义的:
for(var i = 0 ; i < loadedLandmarks.length ; i++)
{
var currentLandmarkName = loadedLandmarks[i].customInfo.Name;
...
对于 for
循环的每次调用,您调用 DisplayDistanceFromLandmarks(pos, i).then()
并在 then
调用中定义一个新的匿名函数。这个函数在创建时有一个与之关联的闭包。在这个闭包中有一个 reference 到 currentLandmarkName
。 注意:这里是对变量的引用而不是它的值。感谢这个参考,你可以使用它。
问题是代码是异步的,所以在for循环结束后调用了then
函数。当 for
循环结束时,currentLandmarkName
始终具有最后一个 loadedLandmarks 的值:
var currentLandmarkName = loadedLandmarks[i].customInfo.Name;
i
等于 loadedLandmarks.length-1
。
所以当 then
函数被调用时,它会尝试访问 currentLandmarkName
的引用,它总是等于最后一个元素:
loadedLandmarks[loadedLandmarks.length-1].customInfo.Name;
因此你总是得到 [insert here the LAST currentLandmarkName]
。
var i = 0;
var DisplayDistance = function () {
var currentLandmarkName = loadedLandmarks[i].customInfo.Name;
landmarksName.push(loadedLandmarks[i].customInfo.Name);
//landmarkName contains the good names, for example : "My school", "My home", "My favorite nightclub" (here after 3 pass on the loop)
console.log(landmarksName);
//DisplayDistanceFromLandmarks is my method which calls the asynchronous API. It seems OK.
DisplayDistanceFromLandmarks(pos, i).then(function(response) {
//The response variable contains correct informations from the API
var origins = response.originAddresses;
var destinations = response.destinationAddresses;
var results = response.rows[0].elements;
//I explain this line below
console.log(loadedLandmarks)
//Then I'm formatting the result to display it on screen (I only paste here the interesting part)
distances += "<br />At " + results[0].distance.value + "kms from" + currentLandmarkName;
i++;
if (i < loadedLandmarks.length) {
DisplayDistance();
}
return distances;
}).done( /*some other things*/ );
}
DisplayDistance();
我正在做一个个人项目,我被困在似乎是 "basic" 同步/异步 Javascript 的问题上。
总而言之,我正在调用异步 API,然后在屏幕上显示结果。
格式化的期望结果是:"At X kilometers from Y",其中 X 是从 API 计算并正确返回的,Y 是地名(未正确显示)。
这是我的代码,为了更好地理解,有一些注释:
//loadedLandmarks.length is **always** between 1 and 3. No exception.
//In this sample, let's say we have 3 items in loadedLandmarks
for(var i = 0 ; i < loadedLandmarks.length ; i++)
{
var currentLandmarkName = loadedLandmarks[i].customInfo.Name;
landmarksName.push(loadedLandmarks[i].customInfo.Name);
//landmarkName contains the good names, for example : "My school", "My home", "My favorite nightclub" (here after 3 pass on the loop)
console.log(landmarksName);
//DisplayDistanceFromLandmarks is my method which calls the asynchronous API. It seems OK.
DisplayDistanceFromLandmarks(pos, i).then(function(response) {
//The response variable contains correct informations from the API
var origins = response.originAddresses;
var destinations = response.destinationAddresses;
var results = response.rows[0].elements;
//I explain this line below
console.log(loadedLandmarks)
//Then I'm formatting the result to display it on screen (I only paste here the interesting part)
distances += "<br />At " + results[0].distance.value + "kms from" + currentLandmarkName;
return distances;
}).done( /*some other things*/ );
显示的结果是:
At 5 kms from [insert here the LAST currentLandmarkName]
At 8.5 kms from [insert here the LAST currentLandmarkName]
At 0.2 kms from [insert here the LAST currentLandmarkName]
而应该是:
At 5 kms from [insert here the FIRST currentLandmarkName]
At 8.5 kms from [insert here the SECOND currentLandmarkName]
At 0.2 kms from [insert here the THIRD currentLandmarkName]
我不明白的是,当我写console.log(loadedLandmarks)
时,数组的内容是正确的,loadedLandmarks[0].Name
=名字,loadedLandmarks[1].Name
=第二个名字等等
但是,i
始终等于 3,而 currentLandmarkName
始终等于最后一个地标名称。
好像被覆盖了,我不明白为什么。
我是 JS 和异步问题的新手,有人可以解释我为什么会遇到这种行为,而且非常重要的是,如何纠正它?
此方法 DisplayDistanceFromLandmarks
是异步的,这意味着它不会阻止其余代码的执行。所以当你像这样在回调中形成 html 字符串 distances
时:
distances += "<br />At " + results[0].distance.value + "kms from" + currentLandmarkName;
for
循环已经结束,回调函数调用已排队,稍后 return 它们的值。回调函数在其范围内没有 currentLandmarkName
,因为它定义为
var currentLandmarkName = loadedLandmarks[i].customInfo.Name;
我觉得你可以做两件事:
- 将变量
currentLandmarkName
传递给DisplayDistanceFromLandmarks
方法,从而将其引入作用域。 - 您也可以尝试使用 while 循环并增加计数器,即仅在回调内增加变量
i
。这样你就强制异步行为是同步的。因此,循环的下一次迭代只会在解决承诺并评估回调后发生。
我看到了一篇非常好的文章,它解释了 javascript 的异步行为和闭包的概念。你可能想读一读 http://www.javascriptissexy.com/understand-javascript-closures-with-ease/
希望它能让您朝着正确的方向开始。干杯!
嗨, 这是 JavaScript 和闭包的典型问题。
在传递给 DisplayDistanceFromLandmarks(pos, i).then()
的函数中,您使用了变量 currentLandmarkName
,这给您带来了一些麻烦。
这个变量不是在该函数内部定义的,而是在 for
循环中定义的:
for(var i = 0 ; i < loadedLandmarks.length ; i++)
{
var currentLandmarkName = loadedLandmarks[i].customInfo.Name;
...
对于 for
循环的每次调用,您调用 DisplayDistanceFromLandmarks(pos, i).then()
并在 then
调用中定义一个新的匿名函数。这个函数在创建时有一个与之关联的闭包。在这个闭包中有一个 reference 到 currentLandmarkName
。 注意:这里是对变量的引用而不是它的值。感谢这个参考,你可以使用它。
问题是代码是异步的,所以在for循环结束后调用了then
函数。当 for
循环结束时,currentLandmarkName
始终具有最后一个 loadedLandmarks 的值:
var currentLandmarkName = loadedLandmarks[i].customInfo.Name;
i
等于 loadedLandmarks.length-1
。
所以当 then
函数被调用时,它会尝试访问 currentLandmarkName
的引用,它总是等于最后一个元素:
loadedLandmarks[loadedLandmarks.length-1].customInfo.Name;
因此你总是得到 [insert here the LAST currentLandmarkName]
。
var i = 0;
var DisplayDistance = function () {
var currentLandmarkName = loadedLandmarks[i].customInfo.Name;
landmarksName.push(loadedLandmarks[i].customInfo.Name);
//landmarkName contains the good names, for example : "My school", "My home", "My favorite nightclub" (here after 3 pass on the loop)
console.log(landmarksName);
//DisplayDistanceFromLandmarks is my method which calls the asynchronous API. It seems OK.
DisplayDistanceFromLandmarks(pos, i).then(function(response) {
//The response variable contains correct informations from the API
var origins = response.originAddresses;
var destinations = response.destinationAddresses;
var results = response.rows[0].elements;
//I explain this line below
console.log(loadedLandmarks)
//Then I'm formatting the result to display it on screen (I only paste here the interesting part)
distances += "<br />At " + results[0].distance.value + "kms from" + currentLandmarkName;
i++;
if (i < loadedLandmarks.length) {
DisplayDistance();
}
return distances;
}).done( /*some other things*/ );
}
DisplayDistance();