Promise

Promises模型

Promises是一個異步編程模型,它經過一組API來規範化異步操做。node

Promises模型的基本概念能夠總結爲:jquery

  1. Promises 做爲結果或錯誤的佔位符
  2. 提供了一種在結果完成或錯誤發生時的通知方式

對於Promises模型而言,已經提出了多個實現的草案:如Promises/A,Promises/B等等。還有一些能夠參看:http://wiki.commonjs.org/wiki/Promisesgit

Promises/A

簡稱「thenable",事實上,每個Promise對象都有一個方法:then(fulfilledHandler,errorHandler,progressHandler)。由於Promise對象有三個狀態:unfulfilled,fulfilled,failed,也就是未完成的,完成的和失敗的。當一個Promise對象由unfulfilled狀態變爲fulfilled狀態後,fulfilledHandler函數會被調用。當進度事件發生時,progressHandler會被調用。一般progress事件能夠省略。github

jQuery Deferred對象

Deferred概念從jQuery的1.5版本開始引用,它是Promises/A標準的一種衍生實現。經過調用jQuery.Deferred()方法,能夠建立Deferred對象,它是一個能夠鏈式操做的對象,並可以註冊多個回調函數到回調隊列,在異步操做成功或者失敗時轉達消息。編程

defer在英文中的意思是延遲,所以咱們能夠理解爲Deferred對象爲延遲執行的對象。api

Deferred對象有三個動做:resolve(解決)、reject(拒絕)和notify(通知)數組

Deferred對象能夠經過一些方法主動調用這三個動做:deferred.resolve()、deferred.reject()和deferred.notify()。固然,還有一些回調對應不一樣的狀態,如:promise

  • resolved時調用deferred.done()回調;
  • rejected時調用deferred.fail()回調;
  • resolved或者rejected時調用deferred.always()回調;
  • inProgress時調用deferred.progress()回調;
  • resolved、rejected或者inProgress時調用then()回調;

有人可能會問then()和always()有什麼區別?其實這兩個是類似的,只是then函數有兩個參數:resolved時的回調和rejected時的回調,至關於將done()和fail()函數合併了,而always函數只有一個回調函數做爲參數,無論是resolved仍是rejected時,都會執行這個回調。app

 1 jQuery.Deferred = function( func ) {
 2     /* Callback參數
 3     once: 全部回調函數只執行一次
 4     memory: 觸發回調後對象能夠繼續添加函數,添加的函數會立刻被觸發
 5     unique: 保證相同的回調函數只能添加一次
 6     stopOnFalse: 當回調返回false時中止觸發
 7     */
 8         var tuples = [
 9                 // action, add listener, listener list, final state
10                 [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
11                 [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
12                 [ "notify", "progress", jQuery.Callbacks("memory") ]
13             ],
14             state = "pending",
15             promise = {
16                 state: function() {
17                     return state;
18                 },
19                 always: function() {
20                     deferred.done( arguments ).fail( arguments );
21                     return this;
22                 },
23                 then: function( /* fnDone, fnFail, fnProgress */ ) {
24                     var fns = arguments;
25                     return jQuery.Deferred(function( newDefer ) {
26                         jQuery.each( tuples, function( i, tuple ) {
27                             var action = tuple[ 0 ],
28                                 fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
29                             // deferred[ done | fail | progress ] for forwarding actions to newDefer
30                             deferred[ tuple[1] ](function() {
31                                 var returned = fn && fn.apply( this, arguments );
32                                 if ( returned && jQuery.isFunction( returned.promise ) ) {
33                                     returned.promise()
34                                         .done( newDefer.resolve )
35                                         .fail( newDefer.reject )
36                                         .progress( newDefer.notify );
37                                 } else {
38                                     newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
39                                 }
40                             });
41                         });
42                         fns = null;
43                     }).promise();
44                 },
45                 // Get a promise for this deferred
46                 // If obj is provided, the promise aspect is added to the object
47                 promise: function( obj ) {
48                     return obj != null ? jQuery.extend( obj, promise ) : promise;
49                 }
50             },
51             deferred = {};
52 
53         // Keep pipe for back-compat
54         promise.pipe = promise.then;
55 
56         // Add list-specific methods
57         jQuery.each( tuples, function( i, tuple ) {
58             var list = tuple[ 2 ],
59                 stateString = tuple[ 3 ];
60 
61             // promise[ done | fail | progress ] = list.add
62             promise[ tuple[1] ] = list.add;
63 
64             // Handle state
65             if ( stateString ) {
66                 list.add(function() {
67                     // state = [ resolved | rejected ]
68                     state = stateString;
69 
70                 // [ reject_list | resolve_list ].disable; progress_list.lock
71                 }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
72             }
73 
74             // deferred[ resolve | reject | notify ]
75             deferred[ tuple[0] ] = function() {
76                 deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
77                 return this;
78             };
79             deferred[ tuple[0] + "With" ] = list.fireWith;
80         });
81 
82         // Make the deferred a promise
83         promise.promise( deferred );
84 
85         // Call given func if any
86         if ( func ) {
87             func.call( deferred, deferred );
88         }
89 
90         // All done!
91         return deferred;
92     }
jQuery Deferred

此外,還有兩個方法:異步

  • deferred.promise():在原來的deferred對象上返回另外一個deferred對象,後者只開放與改變執行狀態無關的方法(好比done()方法和fail()方法),屏蔽與改變執行狀態有關的方法(好比resolve()方法和reject()方法),從而使得執行狀態不能被改變。
  • deferred.when(): 能夠傳入多個deferred對象,併爲他們指定同一個回調(其實就是deferred隊列管理的功能)。

關於Deferred對象的更多細節請看API: http://api.jquery.com/category/deferred-object/

Node.js和Promises

咱們知道,node.js是異步編程的,對於新手而言,異步編程致使的一個常見的問題就是異步嵌套過深。解決的方法有若干個,一個是使用Event,也就是在合適的時間emit某一個事件,那麼代碼就變爲了一行一行的on('xxx',callback);另外一個解決方法就是使用異步管理的庫,如async等;還有就是咱們如今討論的Promises模型。

Nodejs中比較流行的Promises庫就是Q Library。它的項目主頁是:https://github.com/kriskowal/q

  • Q.fcall():建立Promise對象
  • Q.defer():生成defer對象,能夠經過調用resolve和reject方法等改變對象的狀態
  • Q.all():等待一個數組中的全部Promise對象都執行完畢

一個例子是:

function get_all_the_things(things) {
    var the_promises = [];

    things.forEach(function(thing) {
        var deferred = Q.defer();
        get_a_thing(thing, function(result) {
            deferred.resolve(result);
        });
        the_promises.push(deferred.promise);
    });

    return Q.all(the_promises);
}

 

還有其餘的功能,因爲還不熟悉,就先到這裏。

相關文章
相關標籤/搜索