Reputation: 3559
I've read many answers but I don't understand in this case how I can solve this problem.
I have an array of points and with an external website I can determine if the point is on land or on sea. The problem is that the for
loop doesn't wait for the XHR request.
var points = [
[90, 120],
[80, 120],
[70, 120]
];
var land_or_sea = [];
for(var x= 0; x < points.length; x++) {
var lat_string = points[x][0];
var lng_string = points[x][1];
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if (this.readyState === 4 && this.status === 200){
var obj = JSON.parse(this.responseText);
if (obj.water === true){
land_or_sea.push(x + 1);
}
}
}
xhr.open("GET", "https://api.onwater.io/api/v1/results/" + lat_string + "," + lng_string, true);
xhr.send();
}
How can you see land_or_sea
is an empty array when I should add index of points
if point is on sea but doesn't work because for loop doesn't wait.
I hope that can you help me. Thanks!
Upvotes: 2
Views: 1370
Reputation:
I think your best move is to avoid so much api requests and create one api request that will handle list of coordinates. But i guess this api doesn't support it.
Did you check if the api works ok, since i tried and it gave me one false for water, one null and one 500 error telling me too much requests error.
Another good thing is to use xhr.onload method since you have a problem for each time you move through the function defined for onreadystatechange your x variable increments until 3, so for every subsequent readystatechange (for example final response) you are kept with x = 3, since closure keeps pointer, not a value in time of definition. So if this works it will fill the array with value 4 since you put x + 1 like [4, 4, 4].
To wrap it up, use onload and not onreadystatechange and keep an eye on 429/500 errors. Hope this helps.
Upvotes: 1
Reputation: 71
What you are looking for is a Synchrounous xhr request.
The XMLHttpRequest.open() method full syntax is as follows:
XMLHttpRequest.open(method, url, async, user, password)
You can set the "async" request parameter to "false". If this value is false, the send() method does not return until the response is received.
Upvotes: -1
Reputation: 620
If you have ES7 support you can wrap your entire code inside an asynchronous function and just await the result. Create a promise and use it like this: (again, this only applies if you support ES7)
(async () => {
var points = [
[90, 120],
[80, 120],
[70, 120]
];
var p = (lat_string, lng_string) => new Promise(res => {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if(this.readyState === 4 && this.status === 200){
res(JSON.parse(this.responseText));
}
}
xhr.open("GET", "https://api.onwater.io/api/v1/results/" + lat_string + "," + lng_string, true);
xhr.send();
})
var land_or_sea = [];
for(var x= 0; x < points.length; x++) {
var lat_string = points[x][0];
var lng_string = points[x][1];
var obj = await p(lat_string, lng_string)
if(obj.water === true) {
land_or_sea.push(x + 1);
}
}
})()
Upvotes: 0
Reputation: 186
Instead of using a for loop, you can create a recursive function.
You can initialize the array index to 0 and pass it to the function. After successful response, call the same function again with incremented array index. Stop calling the function once you reach array.length
eg:
var points = [
[90, 120],
[80, 120],
[70, 120]
];
var arrIndex = 0;
var land_or_sea = [];
function callAjaxFunc(arrIndex){
var lat_string = points[arrIndex][0];
var lng_string = points[arrIndex][1];
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if (this.readyState === 4 && this.status === 200){
var obj = JSON.parse(this.responseText);
if (obj.water === true){
land_or_sea.push(x + 1);
if (arrIndex < points.length){
arrIndex++;
callAjaxFunc(arrIndex);
}
}
}
}
xhr.open("GET", "https://api.onwater.io/api/v1/results/" + lat_string + "," + lng_string, true);
xhr.send();
}
callAjaxFunc(arrIndex);
Upvotes: 1