I'm trying to fix this bug in my code and I can't find a solution anywhere so it is driving me crazy. I'm giving up trying it myself and gonna ask everybody here for advice.
I'm making a simple ISS tracker in javascript as a beginners exercise. Fetching the position from an API and plotting it on a map with leaflet.js
Now i'm stuck with drawing a polyline, because I can't return an array of latlngs in a correct format and I don't get why.
So I have an async getISS() function that draws the current position on the map (which works fine) and returns the current timestamp. then I have another async function getISS_at_time() returning the lat,lng position for a specified timestamp. but there is the problem. I need to put all the [lat, lng] positions in to an array to feed it to the L.polyline function but I don't understand how.
async function getISS_at_time(timestamp) {
const api_url =
"https://api.wheretheiss.at/v1/satellites/25544/positions?timestamps=" +
timestamp;
const res = await fetch(api_url);
const data = await res.json();
const lat = data[0].latitude;
const lng = data[0].longitude;
const json_data = '{"lat":' + lat + ', "lng": ' + lng + "}";
return JSON.parse(json_data);
}
async function getISS() {
const result = await fetch(api_url);
const data = await result.json();
const position = [data.latitude, data.longitude];
marker.setLatLng(position);
iss_map.setView(position, 2);
return data.timestamp;
}
getISS().then((timestamp) => {
let start = timestamp - 45 * 60;
for (let i = 0; i < 3; ++i) {
timestamp = start + 60 * i;
var positions = [];
getISS_at_time(timestamp).then((pos) => {
//here i'm getting the lat, lng position and trying to put it in a new array
positions[i] = [pos.lat, pos.lng];
});
}
// this is a test var with a correct array format to feed to the polyline function
var latlngs = [
[38.91, -77.07],
[37.77, -79.43],
[39.04, -85.2],
];
console.log(Array.isArray(positions[0])); // returns false
console.log(positions); // looks exactly the same as console.log(latlngs) in the Chrome console (see img)
console.log(Array.isArray(latlngs[0])); // returns true
console.log(latlngs);
// works fine
var poly = L.polyline(latlngs, { color: "red" }).addTo(iss_map);
// draws nothing!
var poly = L.polyline(positions, { color: "red" }).addTo(iss_map);
});
I also tried with positions.push(pos) instead of positions[i] = pos but without success
-
yes, thank you. I fixed that, but doesn't fix the bugmathiflip– mathiflip2021年07月15日 08:34:19 +00:00Commented Jul 15, 2021 at 8:34
1 Answer 1
Your code is accessing positions without awaiting that the promises (returned by getISS_at_time) are resolved.
Here is how you can fix that:
getISS().then((timestamp) => {
let start = timestamp - 45 * 60;
return Promise.all(Array.from({length: 3}, (_, i) => {
return getISS_at_time(start + 60 * i).then((pos) => [pos.lat, pos.lng]);
}));
}).then(positions => {
console.log(Array.isArray(positions[0]));
console.log(positions);
var poly = L.polyline(positions, { color: "red" }).addTo(iss_map);
// ... more code working on this data
});
As you already use async await syntax, you can also use an immediately invoked async function to do the same:
(async function () {
let timestamp = await getISS();
let start = timestamp - 45 * 60;
let positions = await Promise.all(Array.from({length: 3}, async (_, i) => {
let pos = await getISS_at_time(start + 60 * i);
return [pos.lat, pos.lng];
}));
console.log(Array.isArray(positions[0]));
console.log(positions);
var poly = L.polyline(positions, { color: "red" }).addTo(iss_map);
// ... more code working on this data
})(); // immediately invoked
Other remark
It is really bad practice to construct JSON format with string concatenation:
const json_data = '{"lat":' + lat + ', "lng": ' + lng + "}";
return JSON.parse(json_data);
Instead, just construct the object -- and you can even use short-cut object literal syntax for that:
return { lat, lng };
4 Comments
(_, i) but it makes sense that I have to return positions in the first .then() and add a second .then() But if I just use my code with the for loop to call multiple getISS_at_time() in the first .then() and return positions to a second .then() , it should also work, no?for loop will not work, because that will just iterate without waiting that these promises get resolved. Promise.all will take all those promises as argument, and will resolve when all of them have resolved. Only at that moment should the code continue to actually work with positions.Promise.all to understand why it works now :) Thanks for your answers!