Использование async/await для получения данных из обратного вызова и возврата значения только после разрешения обещания

Я пытаюсь использовать async / await, но я думаю, что я что-то неправильно понял.

в основном, насколько это возможно, я пытаюсь рассчитать расстояние между списком местоположений и одним назначенным местоположением с помощью Google maps api.

вот пример того, что я пытаюсь сделать:https://jsfiddle.net/qu5y69rj/1/

вы можете видеть, что результатом этой функции является undefined 3 раза вместо того что я ожидаю, что будет будь {distance: "ZERO_RESULTS"} для каждого вызова в случае моего надуманного примера.

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

Почему результат возвращается перед обещание решить? Как я могу вернуть значение result только после того, как это обещание будет решена? Насколько я понимаю, говоря javascript ждать, я говорю не двигаться вперед, пока это обещание не будет разрешено. Это неправильно? Я в замешательстве, и это заставляет меня выдергивать волосы. Любая помощь приветствуется.

2 ответов


на service.getDistanceMatrix принимает обратный вызов, что означает, что ti, скорее всего, не возвращает обещание.

однако асинхронные функции ожидают обещаний.

в качестве исправления вы можете обернуть getDistanceMatrix это в обещании (или используйте другой метод, который возвращает обещание):

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

существует три способа выполнения асинхронных операций с JavaScript:

  1. обратные вызовы: функция принимает обратный вызов в качестве своего окончательного аргумента. Он ничего не возвращает (undefined), а если асинхронная операция завершается, вызывается.
  2. обещания: функция возвращает обещание, которое разрешает результат асинхронной операции, когда она завершается.
  3. Async / Await: функция возвращает обещайте, и можете использовать async ключевое слово, чтобы получить значения асинхронных операций внутри его определения. Все, что возвращается с помощью return ключевое слово будет завернуто в обещание.

С getDistanceMatrix принимает обратный вызов, он ничего не возвращает. The await ключевое слово, используемое в коде, не нужно ждать; он сразу получает undefined значением, возвращенным getDistanceMatrix. Когда операция завершается и вызывается обратный вызов,getDistance давно закончил выполнение и возвращенный.

вам нужно обернуть getDistanceMatrix таким образом, он возвращает обещание, сделайте getAllDistance() вернуть обещание, а также, и ждать, что обещание в вашем console.log() о себе:

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