#9 异步路由

英文原版:https://guides.emberjs.com/v2.13.0/routing/asynchronous-routing/

本节涵盖了一些router的高级功能,即异步逻辑处理能力。

聊聊Promises

Ember的router在处理异步逻辑的时候大量使用了Promise。简而言之,promise是表示最终结果的对象。一个promise可以被完成(即 resolve)或被拒绝( 即 reject )。处理完成和拒绝状态的方式是通过promise的then()函数,它接受2个函数为参数,一个为当完成promise时被调用,另一个在promise被拒绝是被调用。当promise被完成时,第一个函数将被调用,并且结果作为唯一的参数被传入函数;当promise被拒绝时,第二个函数被调用,并且被拒绝的原因作为唯一的参数被传入函数。看下例:

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

实际上promise的主要影响力是可以通过链式调用的方式来按顺序执行异步操作,即 异步操作同步化

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

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

在上面的例子中,如果当中的任何一个回调函数:fetchPhotosOfUsers、applyInstagramFilters、uploadTrendyPhotoAlbum 返回了一个被拒绝的结果,handleErrors 就会被调用。通过这种方式, promise变成了类似于try…catch语句结构的异步操作,同时终结了传统的内层函数要不断的右缩进的烦恼。并且可以更有条理的管理复杂的异步逻辑。

本教程不会深入讨论promise的各种用法,但是如果你想要了解更多,请阅读RSVP, 这个是Ember的promise库。

因Promise而暂停

当你在路由间穿梭时,Ember router会搜集所有的model数据,并在transtion即将结束时把它们传入路由的controller中。如果model( )钩子(也包含 beforeModel和afterModel)返回了正常的对象(非promise)或数组,本次transition会立即完成。但是如果model( )钩子(也包含 beforeModel和afterModel)返回了一个promise(或者promise作为transitionTo( )的参数),那么本次transition会在promise返回一个值( 无论 resolve 还是 reject )之前一直暂停。

router会把任何带有then()函数的对象当做是promise。

如果promise完成,那么transition会从它停止的地方继续执行,并且会继续执行子路由,如果子路由也有promise,那么继续暂停,直到最终执行到最末端的路由。并且每层路由都会调用它的setupController()钩子,并将promise完成时的返回值作为参数传入。

一个基本的例子:

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

当访问tardy路由时,model( )钩子将被调用并且返回一个promise,这个promise会在3s后被resolve,在这期间router将会暂停。当promise最终被resolve,router会继续执行并最终调用setupController(),并传入被resolve的对象。

When Promise Reject…

我们已经介绍了当promise被resolve时的情况,how about if it rejects?

默认的,当promise被reject时,当前的transition会被终止,不会有新的目标模板被渲染,并且一条错误日志会被显示在控制台。

你可以通过error( ) action函数来处理这段逻辑。当promise被reject后,会在路由中触发一个error事件,并且如果没有在沿途的路由中定出error处理函数,那么这个事件会一直冒泡到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;
    }
  }
});

在上面的例子中,error事件会在error处理函数执行完后终结,并且不会冒泡。如果你想要让error事件冒泡,在error处理函数中返回 true 。

从reject中恢复

promise被reject会导致当前的transition终止,不过由于promise是可以链式调用的,所以你可以model( )中捕获到被reject的promise并且将结果反转为完成的,并且会继续当前的transition:

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

本章完

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章