是什么
Promise是一種對異步操作的封裝,主流的規范是Promise/A+。
Promise可以使得異步代碼層次清晰,便于理解,且更加容易維護。
追求的效果
那么我們的Promise將要實現怎樣的效果呢?舉個簡單的例子如下:
function p1() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(1);
}, 1000);
});
}
function p2(value) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(2 + value);
}, 1000);
});
}
p1().then(function(res) {
console.log(res); // 1000ms后輸出1
return Promise.resolve(res); // 顯式的return一個Promise對象
}).then(p2).then(function(res) {
console.log(res); // 再過1000ms后輸出3
});
我們手寫的Promise最終也可以實現這個效果
最基本的構建
function Promise(fn) {
let value = null; // 異步函數執行后的結果
let deferred; // 異步函數執行后,真正要執行的回調函數
this.then = function(onFulfilled) {
deferred = onFulfilled;
}
function resolve(newValue) {
value = newValue;
deferred(value);
}
fn(resolve);
}
代碼很少,也很容易理解:
- 創建Promise實例的參數是fn,并將其內部的resolve方法作為參數傳遞給異步函數
- Promise的then方法用于注冊回調函數,即賦值給內部的deferred
- 當異步函數回調成功后,會將結果作為參數來執行resolve,而實際上是執行deferred函數
- 這樣就實現了在恰當的時機(異步函數回調成功后),執行恰當的回調(then注冊的回調方法)
改進1
目前的代碼只能注冊一個回調方法,這顯然不符合我們的預期,所以將內部的deferred修改為deferreds數組,相應的執行resolve時,也要遍歷deferreds數組依次執行:
function Promise(fn) {
let value = null;
let deferreds = [];
this.then = function(onFulfilled) {
deferreds.push(onFulfilled);
}
function resolve(newValue) {
value = newValue;
deferreds.forEach((deferred) => {
deferred(value);
});
}
fn(resolve);
}
改進2
實現then的鏈式調用,非常簡單:
this.then = function(onFulfilled) {
deferreds.push(onFulfilled);
return this;
}
這樣就可以實現:
p1().then(function(res) {
// do sth. with res
}).then(function(res) {
// do sth. else with res
});
延時resolve
目前的Promise有一個bug,假如fn中所包含的是同步代碼,則resolve會立即執行,此時then還沒有注冊回調函數,內部的deferreds為空數組,所以回調函數不會如預期一樣執行。
所以,為resolve添加一個延時:
function resolve(newValue) {
value = newValue;
setTimeout(() => {
deferreds.forEach((deferred) => {
deferred(value);
});
}, 0);
}
以上保證了resolve于then注冊回調函數之后執行。
引入狀態
目前還存在一點問題,現在用then注冊回調函數的行為都是在異步操作成功之前,一旦異步操作已經成功后,內部resolve已經執行完畢,再用then方法注冊回調函數就不會再執行了。
想要解決這個問題,需要引入規范中的三個狀態:pending、fulfilled、rejected,它們之間的關系是:
Promise的三種狀態
引入狀態后的代碼:
function Promise(fn) {
let state = "pending";
let value = null;
let deferreds = [];
this.then = function(onFulfilled) {
// state若為pending則將onFulfilled加入隊列
if(state === "pending") {
deferreds.push(onFulfilled);
return this;
}
// state若為fulfilled則立即執行onFulfilled
onFulfilled(value);
return this;
}
function resolve(newValue) {
state = "fulfilled"; // 異步操作完成后將state置為fulfilled
value = newValue;
setTimeout(() => {
deferreds.forEach((deferred) => {
deferred(value);
});
}, 0);
}
fn(resolve);
}
異步操作成功之后,state會變成fulfilled,這之后then注冊的回調函數都會立即執行。
以上基本實現了Promise的所有基礎功能,但真正魔幻般的部分尚未開始,詳見下篇~
參考資料:剖析 Promise 之基礎篇