Angular中的$q的形象解釋及深刻用法

做者:寸志
連接:https://zhuanlan.zhihu.com/p/19622332
來源:知乎
著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。

javascript

早上,老爸說:「兒子,天氣如何?」

每週一早上,老爸問兒子下午的天氣狀況,兒子能夠到自家房子旁邊小山上使用望遠鏡來觀看。兒子在出發時許諾(Promise)老爸(會通知老爸天氣狀況)。
此刻,老爸決定,若是天氣不錯,明天就出去捕魚,不然就不去。並且若是兒子沒法得到天氣預報的話,也不去捕魚。
30分鐘左右,兒子回來了,每週的結局都不同。

html

結局A:成功得到了(retrieved)天氣預報,晴天 :)

兒子成功獲取了天氣預報,天空晴朗,陽光明媚!承諾(Promise)兌現了(resolved),因而老爸決定開始爲週日的捕魚作準備。java

結局B:一樣成功得到了天氣預報,雨天:(

兒子成功得到了天氣預報,只不過是烏雲密佈,要下雨承諾(Promise)兌現了(resolved),只是老爸決定呆在家裏,由於天氣很糟糕。angularjs

結局C:無法得到天氣預報:-/

出了問題,兒子無法得知天氣預報,由於霧很大,就算站在小山上也沒法看清。兒子沒辦法對象他離開時許下的諾言, promise was rejected!老爸決定留下來,這並不值得冒險。編程

從編程的角度來看

在這種狀況下,老爸是邏輯控制,他把兒子當作service來處理。
這裏面的邏輯咱們已經知道了,老爸要兒子給他預報天氣,可是兒子無法立刻告訴他,因而許諾他會帶着天氣預報回來,老爸在兒子回來以前有其餘的事情要作。當老爸從兒子那得到天氣預報以後,再決定是打包出發仍是呆在家裏。這個故事的重點是,兒子去山頂上看天氣這件事並無妨礙(block)老爸幹其餘的事情。所以對於這種狀況,最好的方式就是許諾,隨後再兌現或者不兌現。
咱們但是使用Angular的then()指定老爸針對每種結果的對策。then()函數接受兩個函數做爲參數:一個許諾對現時執行,一個在沒法對現時執行。

api

Controller:FatherCtrl

老爸控制狀況:數組

// function somewhere in father-controller.js
var makePromiseWithSon = function () {
  // This service's function returns a promise, but we'll deal with that shortly
  SonService.getWeather()
  // then() called when son gets back
  .then(function (data) {
    // promise fulfilled
    if (data.forecast === 'good') {
      prepareFishingTrip();
    } else {
      prepareSundayRoastDinner();
    }
  }, function (error) {
    // promise rejected, could log the error with: console.log('error', error);
    prepareSundayRoastDinner();
  });
};

 

Service:SonServicepromise

兒子就是一個服務,他爬上小山,嘗試預報天氣。咱們能夠把兒子經過望遠鏡查看即將到來的天氣,假設成條用一個天氣的API,一般是異步的。要麼得到答案,要麼出現問題(比方說返回500,大霧皚皚的天空)。
「捕魚天氣API」經過一個許諾返回結果,若是兌現的話,結果的格式像 { "forecast": "good" }這樣。app

app.factory('SonService', function ($http, $q) {
  return {
    getWeather: function () {
      // the $http API is based on the deferred/promise APIs exposed by the $q service
      // so it returns a promise for us by default
      return $http.get('http://fishing-weather-api.com/sunday/afternoon')
        .then(function (response) {
          if (typeof response.data === 'object') {
            return response.data;
          } else {
            // invalid response
            return $q.reject(response.data);
          }

        }, function (response) {
          // something went wrong
          return $q.reject(response.data);
        });
    }
  };
});

 

總之

老爸讓兒子預報的天氣就是一個比喻,說明了異步的本質。老爸並不想在門口傻等着兒子回來,他還有別的事情要作。而是在門口許個諾言,決定在3種不一樣的狀況(天氣好/壞/不知道)下該怎麼辦,兒子離開時就許下一個諾言,在他回來的時候會兌現或者食言。異步

兒子須要處理一個異步的API(經過望遠鏡來查看天空/使用天氣API)來得到天氣,但這些對他老爸來講都是未知的,是誰不懂科技!?

原文:Promises in AngularJS, Explained as a Cartoon

 

擴展: 看完以後對$q的用法想深刻了,接着看下面,

這篇文章主要介紹了Angular中的Promise對象($q介紹),本文講解了Promise模式、Q Promise的基本用法、AngularJs中的$q.defferd等內容,須要的朋友能夠參考下
 

在用JQuery的時候就知道 promise 是 Js異步編程模式的一種模式,可是不是很明白他跟JQuery的deferred對象有什麼區別。隨着公司項目的進行,要跟後臺接數據了,因此決定搞定它。

Promise

Promise是一種模式,以同步操做的流程形式來操做異步事件,避免了層層嵌套,能夠鏈式操做異步事件。

咱們知道,在編寫javascript異步代碼時,callback是最最簡單的機制,但是用這種機制的話必須犧牲控制流、異常處理和函數語義化爲代價,甚至會讓咱們掉進出現callback大坑,而promise解決了這個問題。

ES6中Promise、angularJS內置的AngularJS內置Q,以及when採用的都是Promises/A規範,以下:

每一個任務都有三種狀態:未完成(pending)、完成(fulfilled)、失敗(rejected)。

1.pending狀態:能夠過渡到履行或拒絕狀態。
2.fulfilled狀態:不能變爲其餘任何狀態,並且狀態不能改變,必須有value值。
3.rejected狀態:不能變爲其餘任何狀態,並且狀態不能改變,必須有reason。

狀態的轉移是一次性的,狀態一旦變成fulfilled(已完成)或者failed(失敗/拒絕),就不能再變了。

 

複製代碼代碼以下:

function okToGreet(name){
    return name === 'Robin Hood';
}
 
function asyncGreet(name) {
    var deferred = $q.defer();
 
    setTimeout(function() {
     // 由於這個異步函數fn在將來的異步執行,咱們把代碼包裝到 $apply 調用中,一邊正確的觀察到 model 的改變
        $scope.$apply(function() {
            deferred.notify('About to greet ' + name + '.');
 
            if (okToGreet(name)) {
                deferred.resolve('Hello, ' + name + '!');
            } else {
                deferred.reject('Greeting ' + name + ' is not allowed.');
            }
        });
    }, 1000);
 
    return deferred.promise;
}
 
var promise = asyncGreet('Robin Hood');
promise.then(function(greeting) {
    alert('Success: ' + greeting);
}, function(reason) {
    alert('Failed: ' + reason);
}, function(update) {
    alert('Got notification: ' + update);
});

 

Q Promise的基本用法

上面代碼表示, $q.defer() 構建的 deffered 實例的幾個方法的做用。若是異步操做成功,則用resolve方法將Promise對象的狀態變爲「成功」(即從pending變爲resolved);若是異步操做失敗,則用reject方法將狀態變爲「失敗」(即從pending變爲rejected)。最後返回 deferred.promise ,咱們就能夠鏈式調用then方法。

JS將要有原生Promise,ES6中已經有Promise對象,firefox和Chrome 32 beta版本已經實現了基本的Promise API

AngularJs中的$q.defferd

經過 調用 $q.defferd 返回deffered對象以鏈式調用。該對象將Promises/A規範中的三個任務狀態經過API關聯。

deffered API

deffered 對象的方法

1.resolve(value):在聲明resolve()處,代表promise對象由pending狀態轉變爲resolve。
2.reject(reason):在聲明resolve()處,代表promise對象由pending狀態轉變爲rejected。
3.notify(value) :在聲明notify()處,代表promise對象unfulfilled狀態,在resolve或reject以前能夠被屢次調用。

deffered 對象屬性

promise :最後返回的是一個新的deferred對象 promise 屬性,而不是原來的deferred對象。這個新的Promise對象只能觀察原來Promise對象的狀態,而沒法修改deferred對象的內在狀態能夠防止任務狀態被外部修改。

Promise API

當建立 deferred 實例時會建立一個新的 promise 對象,並能夠經過 deferred.promise 獲得該引用。

promise 對象的目的是在 deferred 任務完成時,容許感興趣的部分取得其執行結果。

promise 對象的方法

1.then(errorHandler, fulfilledHandler, progressHandler):then方法用來監聽一個Promise的不一樣狀態。errorHandler監聽failed狀態,fulfilledHandler監聽fulfilled狀態,progressHandler監聽unfulfilled(未完成)狀態。此外,notify 回調可能被調用 0到屢次,提供一個進度指示在解決或拒絕(resolve和rejected)以前。
2.catch(errorCallback) —— promise.then(null, errorCallback) 的快捷方式
3.finally(callback) ——讓你能夠觀察到一個 promise 是被執行仍是被拒絕, 但這樣作不用修改最後的 value值。 這能夠用來作一些釋放資源或者清理無用對象的工做,無論promise 被拒絕仍是解決。 更多的信息請參閱 完整文檔規範.

經過then()方法能夠實現promise鏈式調用。

 

複製代碼代碼以下:

promiseB = promiseA.then(function(result) {  
  return result + 1;  
});  
 
// promiseB 將會在處理完 promiseA 以後馬上被處理,  
// 而且其  value值是promiseA的結果增長1

 

$q的其餘方法

$q.when(value):傳遞變量值,promise.then()執行成功回調
$q.all(promises):多個promise必須執行成功,才能執行成功回調,傳遞值爲數組或哈希值,數組中每一個值爲與Index對應的promise對象

 

再深刻點: 

1. $q

$q是Angular的一種內置服務,它可使你異步地執行函數,而且當函數執行完成時它容許你使用函數的返回值(或異常)。

2. defer

defer的字面意思是延遲,$q.defer() 能夠建立一個deferred實例(延遲對象實例)。

deferred 實例旨在暴露派生的Promise 實例,以及被用來做爲成功完成或未成功完成的信號API,以及當前任務的狀態。這聽起來好複雜的樣子,總結$q, defer, promise三者之間的關係以下所示。

var deferred = $q.defer();  //經過$q服務註冊一個延遲對象 deferred
var promise = deferred.promise;  //經過deferred延遲對象,能夠獲得一個承諾promise,而promise會返回當前任務的完成結果

defer的方法:

    1. deferred.resolve(value)  成功解決(resolve)了其派生的promise。參數value未來會被用做promise.then(successCallback(value){...}, errorCallback(reason){...}, notifyCallback(notify){...})中successCallback函數的參數。

    2. deferred.reject(reason)  未成功解決其派生的promise。參數reason被用來講明未成功的緣由。此時deferred實例的promise對象將會捕獲一個任務未成功執行的錯誤,promise.catch(errorCallback(reason){...})。補充一點,promise.catch(errorCallback)實際上就是promise.then(null, errorCallback)的簡寫。

    3. notify(value)  更新promise的執行狀態(翻譯的很差,原話是provides updates on the status of the promise's execution)

defer的小例子:    

複製代碼
function asyncGreet(name) {
  var deferred = $q.defer();  //經過$q.defer()建立一個deferred延遲對象,在建立一個deferred實例時,也會建立出來一個派生的promise對象,使用deferred.promise就能夠檢索到派生的promise。

  deferred.notify('About to greet ' + name + '.');  //延遲對象的notify方法。

  if (okToGreet(name)) {
    deferred.resolve('Hello, ' + name + '!');  //任務被成功執行
  } else {
    deferred.reject('Greeting ' + name + ' is not allowed.');  //任務未被成功執行
  }

  return deferred.promise;  //返回deferred實例的promise對象
}

function okToGreet(name) {
  //只是mock數據,實際狀況將根據相關業務實現代碼
  if(name == 'Superman') return true;  
  else return false;
}

var promise = asyncGreet('Superman');  //得到promise對象
//promise對象的then函數會得到當前任務也就是當前deferred延遲實例的執行狀態。它的三個回調函數分別會在resolve(), reject() 和notify()時被執行
promise.then(function(greeting) {
  alert('Success: ' + greeting);
}, function(reason) {
  alert('Failed: ' + reason);
}, function(update) {
  alert('Got notification: ' + update);
});
複製代碼

3. promise

當建立一個deferred實例時,promise實例也會被建立。經過deferred.promise就能夠檢索到deferred派生的promise。

promise的目的是容許interested parties 訪問deferred任務完成的結果。

按照CommonJS的約定,promise是一個與對象交互的接口,表示一個動做(action)的結果是異步的,並且在任何給定的時間點上可能或不可能完成。(這句話好繞口,個人理解是promise至關於一個承諾,承諾你這個任務在給定的時間點上可能會完成,也可能完成不了。若是完成了那就至關於resolve, 若是未完成就至關於reject。不知道這樣理解對不對?)

promise 的方法:

    1. then(successCallback, errorCallback, nitifyCallback) 根據promise被resolve/reject,或將要被resolve/reject,調用successCallback/errorCallback。

    2. catch(errorCallback)  then(null, errorCallback)的縮寫。

    3. finally(callback, notifyCallback)

補充說明:

    promise.then()會返回一個新的衍生promise,造成promise鏈。例如:

promiseB = promiseA.then(function(result) {
  return result + 1;
});

// promiseB will be resolved immediately after promiseA is resolved and its value
// will be the result of promiseA incremented by 1
相關文章
相關標籤/搜索