和其它框架一樣, ng 提供了廣義的異步回調管理的機制。 $http 服務是在其之上封裝出來的。這個機制就是 ng 的 $q 服務。
不過 ng 的這套機制總的來說實現得比較簡單,按官方的說法,夠用了。
使用的方法,基本上是:
- 通過 $q 服務得到一個 deferred 實例
- 通過 deferred 實例的 promise 屬性得到一個 promise 對象
- promise 對象負責定義回調函數
- deferred 實例負責觸發回調
var TestCtrl = function($q){ var defer = $q.defer(); var promise = defer.promise; promise.then(function(data){console.log('ok, ' + data)}, function(data){console.log('error, ' + data)}); //defer.reject('xx'); defer.resolve('xx'); }
了解了上面的東西,再分別看 $q , deferred , promise 這三個東西。
11.2.1. $q
$q 有四個方法:
- $q.all() 合並多個 promise ,得到一個新的 promise
- $q.defer() 返回一個 deferred 對象
- $q.reject() 包裝一個錯誤,以使回調鏈能正確處理下去
- $q.when() 返回一個 promise 對象
$q.all() 方法適用於並發場景很合適:
var TestCtrl = function($q, $http){ var p = $http.get('/json', {params: {a: 1}}); var p2 = $http.get('/json', {params: {a: 2}}); var all = $q.all([p, p2]); p.success(function(res){console.log('here')}); all.then(function(res){console.log(res[0])}); }
$q.reject() 方法是在你捕捉異常之后,又要把這個異常在回調鏈中傳下去時使用:
要理解這東西,先看看 promise 的鏈式回調是如何運作的,看下面兩段代碼的區別:
var defer = $q.defer(); var p = defer.promise; p.then( function(data){return 'xxx'} ); p.then( function(data){console.log(data)} ); defer.resolve('123'); var defer = $q.defer(); var p = defer.promise; var p2 = p.then( function(data){return 'xxx'} ); p2.then( function(data){console.log(data)} ); defer.resolve('123');
從模型上看,前者是“並發”,后者才是“鏈式”。
而 $q.reject() 的作用就是觸發后鏈的 error 回調:
var defer = $q.defer(); var p = defer.promise; p.then( function(data){return data}, function(data){return $q.reject(data)} ). then( function(data){console.log('ok, ' + data)}, function(data){console.log('error, ' + data)} ) defer.reject('123');
最后的 $q.when() 是把數據封裝成 promise 對象:
var p = $q.when(0, function(data){return data}, function(data){return data}); p.then( function(data){console.log('ok, ' + data)}, function(data){console.log('error, ' + data)} );
11.2.2. deferred
deferred 對象有兩個方法一個屬性。
- promise 屬性就是返回一個 promise 對象的。
- resolve() 成功回調
- reject() 失敗回調
var defer = $q.defer(); var promise = defer.promise; promise.then(function(data){console.log('ok, ' + data)}, function(data){console.log('error, ' + data)}); //defer.reject('xx'); defer.resolve('xx');
11.2.3. promise
promise 對象只有 then() 一個方法,注冊成功回調函數和失敗回調函數,再返回一個 promise 對象,以用於鏈式調用。
promise是頭等對象,自帶了一些約定。
只有一個resolve或者reject會被調用到:
resolve被調用時,帶有一個履行值;
reject被調用時要帶一個拒絕原因。
如果promise被執行或者拒絕了,依賴於它們的處理程序仍然會被調用;
處理程序總是會被異步調用。