Promise 類似于一個事務(wù)管理器,它的作用就是將各種內(nèi)嵌回調(diào)的事務(wù)用流水形式表達(dá)畦浓。利用 Promise 可以讓異步編程更符合人的直覺,讓代碼邏輯更加清晰,把開發(fā)人員從回調(diào)地獄中釋放出來诗越。
基礎(chǔ)概念
目前, Promise 是 ECMAScript 6 規(guī)范的重要特性之一息堂,各大瀏覽器也開始慢慢支持這一特性嚷狞。當(dāng)然,也有一些第三方內(nèi)庫實(shí)現(xiàn)了該功能荣堰,如: Q 床未、 when 、 WinJS 振坚、 RSVP.js 等薇搁。
Promise 對象用來進(jìn)行延遲( deferred )和異步( asynchronous )計(jì)算。一個 Promise 處于以下四種狀態(tài)之一:
- pending: 還沒有得到肯定或者失敗結(jié)果渡八,進(jìn)行中
- fulfilled: 成功的操作
- rejected: 失敗的操作
- settled: 已被 fulfilled 或 rejected
Promise 對象有兩個重要的方法啃洋,一個是 then 传货,另一個是 resolve :
- then:將事務(wù)添加到事務(wù)隊(duì)列中
- resolve:開啟流程,讓整個操作從第一個事務(wù)開始執(zhí)行
Promise 常用方式如下:
var p = new Promise(function(resolve, reject) {
... // 事務(wù)觸發(fā) resovle(xxx); ...
});
p.then(function(value) { // 滿足 };
function(reason) { // 拒絕 }).then().then()...
實(shí)現(xiàn)步驟
Promise 其實(shí)就是一個狀態(tài)機(jī)宏娄。按照它的定義问裕,我們可從如下基礎(chǔ)代碼開始:
var PENDING = 0; // 進(jìn)行中
var FULFILLED = 1; // 成功
var REJECTED = 2; // 失敗
function Promise() {
var state = PENDING;// 存儲PENDING, FULFILLED或者REJECTED的狀態(tài)
var value = null;// 存儲成功或失敗的結(jié)果值
var handlers = []; // 存儲成功或失敗的處理程序,通過調(diào)用`.then`或者`.done`方法
function fulfill(result) { // 成功狀態(tài)變化
state = FULFILLED;
value = result;
}
function reject(error) { // 失敗狀態(tài)變化
value = error;
}
}
2.下面是 Promise 的 resolve 方法實(shí)現(xiàn):
注意: resolve 方法可接收的參數(shù)有兩種:
- 一個普通的值/對象
- 一個 Promise 對象孵坚。
如果是普通的值/對象粮宛,則直接把結(jié)果傳遞到下一個對象;
如果是一個 Promise 對象十饥,則必須先等待這個子任務(wù)序列完成窟勃。
function Promise(){
...
function resolve(result){
try {
var then = getThen(result);
// 如果是一個promise對象
if (then) {
doResolve(then.bind(result), resolve, reject);
return;
}
// 修改狀態(tài),傳遞結(jié)果到下一個事務(wù)
fulfill(result);
} catch (e) {
reject(e);
}
}
}
兩個輔助方法:
/** * Check if a value is a Promise and, if it is,
* return the `then` method of that promise.
* * @param {Promise|Any} value
* * @return {Function|Null}
* */
function getThen(value) {
var t = typeof value;
if (value && (t === 'object' || t === 'function')) {
var then = value.then;
if (typeof then === 'function') {
return then;
}
}
return null;
}
/** * Take a potentially misbehaving resolver function and make sure
* onFulfilled and onRejected are only called once.
* Makes no guarantees about asynchrony.
* @param {Function} fn A resolver function that may not be trusted
* @param {Function} onFulfilled
* @param {Function} onRejected
* /
function doResolve(fn, onFulfilled, onRejected) {
var done = false;
try {
fn(function(value) {
if (done) return;
done = true;
onFulfilled(value); },
function(reason) {
if (done) return;
done = true;
onRejected(reason); });
} catch(ex) {
if (done) return;
done = true;
onRejected(ex);
}
}
3.上面已經(jīng)完成了一個完整的內(nèi)部狀態(tài)機(jī)逗堵,但我們并沒有暴露一個方法去解析或則觀察 Promise ”酰現(xiàn)在讓我們開始解析 Promise :
function Promise(fn) {
...
doResolve(fn, resolve, reject);
}
如你所見,我們復(fù)用了 doResolve 蜒秤,因?yàn)閷τ诔跏蓟?fn 也要對其進(jìn)行控制汁咏。 fn 允許調(diào)用 resolve 或則 reject 多次,甚至拋出異常作媚。這完全取決于我們?nèi)ケWC promise 對象僅被 resolved 或則 rejected 一次攘滩,且狀態(tài)不能隨意改變。
4.目前纸泡,我們已經(jīng)有了一個完整的狀態(tài)機(jī)漂问,但我們?nèi)匀粵]有辦法去觀察它的任何變化。我們最終的目標(biāo)是實(shí)現(xiàn) then 方法女揭,但 done 方法似乎更簡單蚤假,所以讓我們先實(shí)現(xiàn)它。
我們的目標(biāo)是實(shí)現(xiàn) promise.done(onFullfilled, onRejected) :
- onFulfilled 和 onRejected 兩者只能有一個被執(zhí)行吧兔,且執(zhí)行次數(shù)為一
- 該方法僅能被調(diào)用一次, 一旦調(diào)用了該方法磷仰,則 promise 鏈?zhǔn)秸{(diào)用結(jié)束
- 無論是否 promise 已經(jīng)被解析,都可以調(diào)用該方法
var PENDING = 0; // 進(jìn)行中
var FULFILLED = 1; // 成功
var REJECTED = 2; // 失敗
function Promise() {
var state = PENDING; // 存儲PENDING, FULFILLED或者REJECTED的狀態(tài)
var value = null; // 存儲成功或失敗的結(jié)果值
var handlers = []; // 存儲成功或失敗的處理程序境蔼,通過調(diào)用`.then`或者`.done`方法
// 成功狀態(tài)變化
function fulfill(result) {
state = FULFILLED;
value = result;
handlers.forEach(handle);
handlers = null;
}
// 失敗狀態(tài)變化
function reject(error) {
state = REJECTED;
value = error;
handlers.forEach(handle);
handlers = null;
}
function resolve(result) {
try {
var then = getThen(result);
if (then) {
doResolve(then.bind(result), resolve, reject)
return
}
fulfill(result);
} catch (e) {
reject(e);
}
}
// 不同狀態(tài)灶平,進(jìn)行不同的處理
function handle(handler) {
if (state === PENDING) {
handlers.push(handler);
} else {
if (state === FULFILLED && typeof handler.onFulfilled === 'function') {
handler.onFulfilled(value);
}
if (state === REJECTED && typeof handler.onRejected === 'function') {
handler.onRejected(value);
}
}
}
this.done = function (onFulfilled, onRejected) {
// 保證異步
setTimeout(
function () {
handle({
onFulfilled: onFulfilled,
onRejected: onRejected });
}, 0);
}
doResolve(fn, resolve, reject);
}
當(dāng) Promise 被 resolved 或者 rejected 時,我們保證 handlers 將被通知箍土。
5.現(xiàn)在我們已經(jīng)實(shí)現(xiàn)了 done 方法逢享,下面實(shí)現(xiàn) then 方法就很容易了。需要注意的是吴藻,我們要在處理程序中新建一個 Promise 拼苍。
this.then = function (onFulfilled, onRejected) {
var self = this;
return new Promise(
function (resolve, reject) {
return self.done(
function (result) {
if (typeof onFulfilled === ‘function’) {
try {
// onFulfilled方法要有返回值!
return resolve(onFulfilled(result));
} catch (ex) {
return reject(ex);
}
} else {
return resolve(result);
}
},
function (error) {
if (typeof onRejected === ‘function’) {
try {
return resolve(onRejected(error));
} catch (ex) {
return reject(ex);
}
} else {
return reject(error);
}
});
});
}