1

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

enter image description here

I also tried with positions.push(pos) instead of positions[i] = pos but without success

1
  • yes, thank you. I fixed that, but doesn't fix the bug Commented Jul 15, 2021 at 8:34

1 Answer 1

3

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 };
Sign up to request clarification or add additional context in comments.

4 Comments

ok, if I copy and paste your Promise.all code it works! so thanks for that. But I don't really understand that syntax with the (_, 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?
And thanks for the "other remark", that is way more easy and readable!
The 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.
clear, I will read about Promise.all to understand why it works now :) Thanks for your answers!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.