JavaScript中Promises/A+規范的實現
Promises是一種異步編程模型,通過一組API來規范化異步操作,這樣也能夠讓異步操作的流程控制更加容易。
下面的代碼是假設執行一個異步隊列,每一項都會使用上一項返回的數據:
function nest(url, params, fn) { $.getJSON(url, params, function(data) { console.log(data); fn.call(this); }); }
nest('promise.php', {a:1}, function(data1) { nest('promise.php', {b:2}, function(data2) { nest('promise.php', {c:3}, function(data3) { console.log('同步完成'); }); }); });
這是一個回調金字塔,當異步的任務很多的時候,需要維護大量的callback。這嵌套的自己眼睛都看不清了吧。
Promise/A+規范就是為了解決上面的問題,可以用類似下面的代碼來改進,“nest”也會做些修改:
promise.then(nest('promise.php', {a:1})) .then(nest('promise.php', {b:2})) .then(nest('promise.php', {c:3}));
下圖是改進的圖片示例,我在畫圖的時候也感覺到,左邊的比較難畫,右邊的很好畫。
接下來會圍繞改進金字塔,實現規范展開。
一、Promises/A+規范說明
Promises/A+規范 如下:
1)一個promise可能有三種狀態:等待(pending)、已完成(fulfilled)、已拒絕(rejected)
2)一個promise的狀態只可能從“等待”轉到“完成”態或者“拒絕”態,不能逆向轉換,同時“完成”態和“拒絕”態不能相互轉換
3)promise必須實現then方法,而且then必須返回一個promise
4)同一個promise的then可以調用多次,并且回調的執行順序跟它們被定義時的順序一致
5)then方法接受兩個參數,第一個參數是成功時的回調,在promise由“等待”態轉換到“完成”態時調用
6)另一個是失敗時的回調,在promise由“等待”態轉換到“拒絕”態時調用。
7)then可以接受另一個promise傳入,也接受一個“類then”的對象或方法,即thenable對象。
接下來先實現一個簡易的,只有完成狀態,沒有拒絕和等待狀態。
二、簡單實現
Promise對象的實現:
function Promise(fn) { this._status = 'pending'; this._resolves = []; //隊列 this._fn = fn; return this; } Promise.prototype = { then: function(resolve) { var next = this._next || (this._next = new Promise()); //下一個promise對象 this._resolves.push(resolve); //設置隊列 return next; }, resolved: function(value) { //改變狀態 this._status = 'fulfilled'; this._result = (this._fn && this._fn(value)) || value; while (fn = this._resolves.shift()) { //循環調用隊列 this._fire(this._next, fn); } }, _fire: function(nextPromise, nextFn) { var nextResult = nextFn(this._result); if (nextResult instanceof Promise) { //判斷回調是否是Promise對象 //只有當nextResult的狀態為fulfilled,下一個promise才可以執行 nextResult.then(function(value) { nextPromise.resolved(value); }); } else { nextPromise.resolved(nextResult); } } };
演示用的函數:
function nest2(url, params) { return function(pre) { var promise = new Promise(); $.getJSON(url, params, function(data) { promise.resolved(data); }); return promise; }; } function begin(value) { return value + '!'; }
初始化代碼:
var promise = new Promise(begin); promise.then(nest2('promise.php', {a: 1})) .then(nest2('promise.php', {b: 2})); promise.resolved('開始');
也可以另外一種方式調用,這樣的話內部的_resloves隊列中會有多個值
var promise = new Promise(begin); promise.then(nest2('promise.php', {a: 1})) promise.then(nest2('promise.php', {b: 2})); promise.resolved('開始');
demo下載:
http://download.csdn.net/detail/loneleaf1/9391315
參考資料:
http://www.alloyteam.com/2014/05/javascript-promise-mode/ JavaScript Promise啟示錄
http://www.cnblogs.com/fsjohnhuang/p/4135149.html JS魔法堂:剖析源碼理解Promises/A規范
http://www.cnblogs.com/aaronjs/archive/2012/11/17/2774440.html 使用Promises/A
http://rapheal.sinaapp.com/2013/01/26/jquery-src-deferred/ $.Deferred
http://www.ituring.com.cn/article/66566 Promises/A+規范