0

I am trying to map an array and I want to add a field to that array using spread syntax. The data I want to use for each element is from api call. I'm trying to get the values instead of the promise.

This is what I get now:

​
distance: Promise { <state>: "fulfilled", <value>: (1) […] }

id: "1234"

It should be:

distance: 5

id: "1234"

Here is the code I'm using:

let stations = nearestStations.map(station => ({
  ...station,
  distance: getDistance([userLocation[0], userLocation[1]], [station.lat,station.lon]).then(_=>_)
}))
console.log(stations)
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
akort
  • 11
  • 2
  • If `getDistance` returns a promise, why did you expect anything different? `.then(_=>_)` is a no-op. – jonrsharpe Jun 03 '19 at 07:13
  • Possible duplicate of [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – jonrsharpe Jun 03 '19 at 07:14
  • `let stations = await Promise.all(nearestStations.map(async station => ({ ...station, distance: await getDistance([userLocation[0], userLocation[1]], [station.lat,station.lon]) })))` – dziraf Jun 03 '19 at 07:14
  • Specifically [this answer](https://stackoverflow.com/a/43766002/3001761) for multiple async operations. – jonrsharpe Jun 03 '19 at 07:16

1 Answers1

1

Use Promise.all to wait for each Station promise to resolve:

const userLatLon = [userLocation[0], userLocation[1]];
const stationProms = nearestStations.map(
  // use Promise.all here so that the station can be passed along with its `distance` promise
  station => (Promise.all([station, getDistance(userLatLon, [station.lat,station.lon])]))
);
Promise.all(stationProms).then((stationItems) => {
  const stations = stationItems.map(([station, distance]) => ({ ...station, distance }));
  console.log(stations)
});

The inner Promise.all isn't necessary, but it helps constrain the scope - equivalently, you could do:

const userLatLon = [userLocation[0], userLocation[1]];
const stationProms = nearestStations.map(station => getDistance(userLatLon, [station.lat,station.lon]));
Promise.all(stationProms).then((stationItems) => {
  const stations = stationItems.map((distance, i) => ({ ...nearestStations[i], distance }));
  console.log(stations)
});

Thanks @jonrsharpe, a much nicer-looking approach would just chain a .then onto the getDistance promise:

const userLatLon = [userLocation[0], userLocation[1]];
const stationProms = nearestStations.map(
  station => getDistance(userLatLon, [station.lat,station.lon])
    .then(distance => ({ ...station, distance }))
);
Promise.all(stationProms).then(console.log);
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • The order of the results returned by `Promise.all` is the same as the order of the inputs, so you may as well zip the results with `nearestStations` again when they're done… – deceze Jun 03 '19 at 07:18
  • 1
    That seems like an awkward approach, why not just `Promise.all(nearestStations.map((station) => getDistance(...).then((distance) => ({ ...station, distance }))));` – jonrsharpe Jun 03 '19 at 07:20
  • @jonrsharpe Oh yes, chaining off the `getDistance` call is much better, thanks! – CertainPerformance Jun 03 '19 at 07:23
  • Thanks, indeed the problem was chaining the promises properly. – akort Jun 03 '19 at 08:37