En En

Асинхронная маршрутизация


Этот раздел рассказывает о продвинутых функциях роутера и его возможности обрабатывать сложную асинхронную логику внутри приложения.

Пара слов об обещаниях…

Подход Ember к обработке асинхронной логики в роутере во многом основывается на использовании концепции обещаний. Обещания — объекты, которые представляют значение события. Обещание может завершиться успешно (разрешить значение) или с ошибкой (не разрешить значение). Чтобы вернуть это конечное значение или обработать случаи, когда обещание завершается с ошибкой, нужно использовать метод обещания then(). Он принимает два опциональных обратных вызова: один для успешного завершения и один для выполнения с ошибкой. Если обещание завершается успешно, то запускается обработчик выполнения с соответствующим значением в качестве единственного аргумента. Если возникает ошибка, вызывается обработчик отказа с указанием причины ошибки в качестве единственного аргумента. Например:

var promise = fetchTheAnswer();

promise.then(fulfill, reject);

function fulfill(answer) {
  console.log(`The answer is ${answer}`);
}

function reject(reason) {
  console.log(`Couldn't get the answer! Reason: ${reason}`);
}

Эффективность обещаний заключается в том, что их можно объединять в цепочку для выполнения последовательных асинхронных операций:

// Note: jQuery AJAX methods return promises
var usernamesPromise = Ember.$.getJSON('/usernames.json');

usernamesPromise.then(fetchPhotosOfUsers)
                .then(applyInstagramFilters)
                .then(uploadTrendyPhotoAlbum)
                .then(displaySuccessMessage, handleErrors);

В примере выше, если какой-то из методов fetchPhotosOfUsersapplyInstagramFilters или uploadTrendyPhotoAlbum возвращает завершенное с ошибкой обещания, будет вызван handleErrors с причиной ошибки. Обещания схожи с асинхронной формой операторов обработки исключений тем, что не допускают «смещения вправо» вложенного обратного вызова после вложенного обратного вызова и обеспечивают разумный подход к управлению сложной асинхронной логикой в приложении.

В этом руководстве мы не будем рассматривать все способы использования обещаний, но если вы хотите больше о них узнать, то почитайте о библиотеке обещаний RSVP, которая используется в Ember.

Паузы роутера для обещаний

При переходе между маршрутами роутер Ember собирает все модели (через hook model), которые будут переданы контроллерам маршрута в конце перехода. Если hook model (или соответсвтующие hooks beforeModel или afterModel) возвращают нормальные (не обещания) объекты или массивы, переход завершится сразу. Но если hook model (или соответсвтующие hooks beforeModel или afterModel) возвращает обещание (или если обещание выступало в качестве аргумента к transitionTo), переход будет приостановлен, пока это обещание не завершится успешно или с ошибкой.

Роутер рассматривает любой объект с методом then() как обещание.

Если обещание завершается успешно, переход продолжается с места остановки и начнется разрешение модели следующего (дочернего) маршрута. Переход будет приостановлен, если снова встретится обещание, и так далее, пока не будут разрешены все модели маршрута назначения. Значения, которые переданы hook setupController() для каждого маршрута, будут разрешенными значениями из обещаний.

Базовый пример:

app/routes/tardy.js

import Ember from 'ember';
import RSVP from 'rsvp';

export default Ember.Route.extend({
  model() {
    return new RSVP.Promise(function(resolve) {
      Ember.run.later(function() {
        resolve({ msg: 'Hold Your Horses' });
      }, 3000);
    });
  },

  setupController(controller, model) {
    console.log(model.msg); // "Hold Your Horses"
  }
});

При переходе в route:tardy будет вызван hook model(). Он вернет обещание, которое не будет разрешено, пока не пройдет 3 секунды. В течение этого времени роутер будет приостановлен на середине перехода. Когда обещание разрешится, роутер продолжит переход и вызовет hook setupController() в route:tardy с разрешенным объектом.

Такое поведение с паузой на обещании имеет большое значение, когда вам нужно, чтобы данные маршрута полностью загрузились до отображения нового шаблона.

Когда обещания завершаются с ошибкой…

Мы рассмотрели случай, когда обещание завершается успешно. Но что если оно завершается с ошибкой?

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

Можно настроить логику обработки ошибки через обработчика error в хеше маршрута actions. Когда обещание завершается с ошибкой, на маршруте возникает событие error, которое распространяется до исходного обработчика ошибок в route:application, если попутно его не встретит пользовательский обработчик ошибок. Например:

app/routes/good-for-nothing.js

import Ember from 'ember';
import RSVP from 'rsvp';

export default Ember.Route.extend({
  model() {
    return RSVP.reject("FAIL");
  },

  actions: {
    error(reason) {
      alert(reason); // "FAIL"

      // Can transition to another route here, e.g.
      // this.transitionTo('index');

      // Uncomment the line below to bubble this error event:
      // return true;
    }
  }
});

В примере выше событие ошибки остановится прямо в обработчике ошибок в route:good-for-nothing и не распространится дальше. Чтобы событие распространилось дальше на route:application, можно вернуть значение true из обработчика ошибок.

Восстановление после завершения с ошибкой

Завершенные с ошибкой обещания модели удерживают переходы. Но, так как обещания связаны в цепочку, можно перехватить обещания с ошибкой в самом hook model и преобразовать их в успешно завершенные, которые не будут задерживать переход.

app/routes/funky.js

import Ember from 'ember';

export default Ember.Route.extend({
  model() {
    return iHopeThisWorks().catch(function() {
      // Promise rejected, fulfill with some default value to
      // use as the route's model and continue on with the transition
      return { msg: 'Recovered from rejected promise' };
    });
  }
});

Комментарии (0)

    Выделите опечатку и нажмите Ctrl + Enter, чтобы отправить сообщение об ошибке.