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

本章完

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