简体   繁体   中英

Using async/await to get data from a callback and return a value only after a promise is resolved

I'm trying to use async/await but I think I'm misunderstanding something critically.

As basically as possible, I am trying to calculate the distance between a list of locations and one designated location using google maps api.

Here is a rough example of what I'm trying to do: https://jsfiddle.net/qu5y69rj/1/

You can see that the result of that function is undefined 3 times instead of what I would expect which would be {distance: "ZERO_RESULTS"} for each call in the case of my contrived example.

getDistance = async (start, end) => {
  const origin = new google.maps.LatLng(start[0], start[1]);
  const final = new google.maps.LatLng(end[0], end[1]);
  const service = new google.maps.DistanceMatrixService();
  let result; //need to return this value!
  await service.getDistanceMatrix(
    {
        origins: [origin],
      destinations: [final],
      travelMode: 'DRIVING'
    }, (response, status) => {
      if(status === 'OK') result = {distance: response.rows[0].elements[0].status}
    }
  )
  return result;
}

Why is result being returned before the promise is resolved? How can I return the value of result only after that promise is resolved? It is my understanding that by telling javascript to await, I'm saying don't move forward until this promise has resolved. Is that incorrect? I'm pretty confused and this has me pulling my hair out. Any help is appreciated.

The service.getDistanceMatrix accepts a callback which means ti most likely doesn't return a promise.

However, async functions expect promises.

As a fix, you can wrap getDistanceMatrix it in a promise (or use another method that does return a promise):

const getDistanceMatrix = (service, data) => new Promise((resolve, reject) => {
  service.getDistanceMatrix(data, (response, status) => {
    if(status === 'OK') {
      resolve(response)
    } else {
      reject(response);
    }
  })
});

getDistance = async (start, end) => {
  const origin = new google.maps.LatLng(start[0], start[1]);
  const final = new google.maps.LatLng(end[0], end[1]);
  const service = new google.maps.DistanceMatrixService();
  const result = await getDistanceMatrix(
    service,
    {
      origins: [origin],
      destinations: [final],
      travelMode: 'DRIVING'
    }
  )
  return {
    distance: result.rows[0].elements[0].status
  };
};

There are three ways to do async operations with JavaScript:

  1. Callbacks : A function accepts a callback as its final argument. It returns nothing ( undefined ), and when the async operation completes, the callback is called.
  2. Promises : A function returns a promise, which resolves to the result of the async operation when it completes.
  3. Async/Await : A function returns a promise, and can use the async keyword to get the values of async operations inside its definition. Whatever is returned using the return keyword will be wrapped in a promise.

Since getDistanceMatrix accepts a callback, it returns nothing. The await keyword as used in the code doesn't need to wait; it immediately gets the undefined value returned by getDistanceMatrix . When the operation completes and the callback is called, the getDistance has long finished executing and returned.

You need to wrap getDistanceMatrix so it returns a promise, make getAllDistance() return a promise as well, and await that promise in your console.log() statement:

const coords = [
  ['-36.22967', '-125.80271'],
  ['54.06395', '54.06395'],
  ['-5.00263', '-137.92806']
];

function getDistance (start, end) {
  const origin = new google.maps.LatLng(start[0], start[1]);
  const final = new google.maps.LatLng(end[0], end[1]);
  const service = new google.maps.DistanceMatrixService();

  return new Promise((resolve, reject) => {
    service.getDistanceMatrix(
    {
        origins: [origin],
      destinations: [final],
      travelMode: 'DRIVING'
    }, (response, status) => {
      if(status === 'OK') {
        resolve({ distance: response.rows[0].elements[0].status });
      } else {
        reject(new Error('Not OK'));
      }
    }
  );
  });
}

function getAllDistance (starts, end) {
  const promisedDistances = starts.map((start) => getDistance(start, end));
  // Promise.all turns an array of promises into a promise
  // that resolves to an array.
  return Promise.all(promisedDistances);
}

getAllDistance(coords, ['-30.23978', '-161.31203'])
  .then(result => { console.log(result); });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM