Несущественные обещания Angular

Я пытаюсь переписать код для http://m.amsterdamfoodie.nl в более современном стиле. По сути, одностраничное приложение Angular загружает набор ресторанов с указанием местоположений и размещает их на карте. Если пользователь находится в районе Амстердама, то также добавляется местоположение пользователя, а также расстояния до мест.

В настоящее время я управляю асинхронными возвратами, используя много if (relevant object from other async call exists) then do next step. Я хотел бы больше использовать обещания было бы лучше.

Итак, управление потоком должно быть:

  • Начать загрузку данных ajax и вызов геолокации
  • если геолокация вернется первой, сохраните координаты на потом
  • once ajax data is downloaded
    • if geolocation available
      • calculate distances to each restaurant, and pass control to rendering code
    • иначе передать управление немедленно для рендеринга кода
  • если геолокация разрешится позже, рассчитайте расстояния и повторите рендеринг

Шаблоны, которые я нахожу в Интернете, предполагают, что все асинхронные вызовы должны быть успешно возвращены, прежде чем продолжить, тогда как мой вызов геолокации может завершиться ошибкой (или вернуть местоположение далеко от Амстердама), и это нормально. Есть ли трюк, который я мог бы использовать в этом сценарии, или условные операторы действительно подходят?


person Simon H    schedule 04.11.2014    source источник
comment
Ваше обещание по-прежнему может быть resolve успешно выполнено, даже если данные отрицательны или отсутствуют. Считайте это успешным результатом. Обрабатывайте reject аналогично catch в try/catch блоках и обрабатывайте неустранимые исключения.   -  person New Dev    schedule 04.11.2014
comment
Как проверить, выполнено ли обещание геолокации после того, как у меня есть данные ресторана. Если она не решена, я не собираюсь ее ждать (поскольку в результате этого человека все равно не будет в Амстердаме).   -  person Simon H    schedule 05.11.2014
comment
Может быть, я что-то упускаю, но вы можете решить это, получили ли вы данные или получили отрицательный результат. Это будет вкладом в ваш следующий шаг.   -  person New Dev    schedule 05.11.2014
comment
То, что я искал, было чем-то более элегантным, чем три оператора if, теперь выделенные жирным шрифтом, которые использовали управление потоком угловых $http и $q. Это может быть желаемое за действительное?   -  person Simon H    schedule 05.11.2014


Ответы (2)


Каждый раз, когда вы используете .then, вы, по сути, создаете новое обещание на основе предыдущего обещания и его состояния. Вы можете использовать это в своих интересах (и вы должны).

Вы можете сделать что-то вроде:

function getGeolocation() {
  return $http.get('/someurl').then(
    function resolveHandler(response) {
      // $http.X resolves with a HTTP response object.
      // The JSON data is on its `data` attribute
      var data = response.data;

      // Check if the data is valid (with some other function).
      // By this, I mean e.g. checking if it is "far from amsterdam",
      // as you have described that as a possible error case
      if(isValid(data)) {
        return data;
      }
      else {
        return null;
      }
    },
    function rejectHandler() {
      // Handle the retrieval failure by explicitly returning a value
      // from the rejection handler. Null is arbitrarily chosen here because it
      // is a falsy value. See the last code snippet for the use of this
      return null;
    }
  );
}

function getData() {
  return $http.get('/dataurl').then(...);
}

а затем используйте $q.all для обоих промисов, что, в свою очередь, создает новый обещание, которое разрешается, как только все данные обещания разрешаются.

Примечание. В вопросе Криса Ковала, который сервиса Angular $q можно использовать метод allSettled, который делает почти то же самое, что и all, но разрешается, когда все обещания исполнены (выполнены или отклонены), а не только если все обещания выполнены. Angular $q не предоставляет этот метод, поэтому вы можете вместо этого обойти это, явно разрешив неудачный http-запрос.

Итак, вы можете сделать что-то вроде:

$q.all([getData(), getGeolocation()])
.then(function(data, geolocation) {
  // `data` is the value that getData() resolved with,
  // `geolocation` is the value that getGeolocation() resolved with.
  // Check the documentation on `$q.all` for this.
  if(geolocation) {
    // Yay, the geolocation data is available and valid, do something
  }

  // Handle the rest of the data
});
person yerforkferchips    schedule 04.11.2014

Может быть, я что-то упускаю... но поскольку у вас нет зависимостей между двумя асинхронными вызовами, я не понимаю, почему вы не можете просто следовать изложенной вами логике:

var geoCoordinates = null;
var restaurants = null;
var distances = null;

getRestaurantData()
  .then(function(data){
     restaurants = data;
     if (geoCoordinates) {
        distances = calculate(restaurants, geoCoordinates);
     }
     // set $scope variables as needed
});

getGeoLocation()
  .then(function(data){
     geoCoordinates = data;
     if (restaurants){
        distances = calculate(restaurants, geoCoordinates)
     }
     // set $scope variables as needed
  });
person New Dev    schedule 05.11.2014