Promise標准
不能免俗地貼個Promise標准鏈接Promises/A+。ES6的Promise有很多方法,包括Promise.all()/Promise.resolve()/Promise.reject()等,但其實這些都是Promises/A+規范之外的,Promises/A+規范只定義了一個Promise.then()方法,這是Promise的核心。
基本結構
new Promise((resolve, reject) => {
let a = 0;
if (a > 1) {
resolve(a);
} else {
reject(a);
}
}).then(res => {
console.log(res);
}, err => {
console.log(err);
})
Promise接收一個函數作為參數,我們稱之為executor,該函數有兩個參數resolve和reject,這兩個參數也都是函數,並且,它們定義在Promise內部。
那么我們定義一個class並定義一個_isFunction方法,用來校驗構造函數的參數必須是函數。再定義resolve和reject這兩個方法。
class MyPromise{
constructor(executor){
if(!this._isFunction(executor)){
throw new Error(`Promise resolver ${executor} is not a function`);
}
}
_isFunction(val){
return Object.prototype.toString.call(val) === '[object Function]';
}
_resolve(){
}
_reject(){
}
}
Promise狀態、resolve、reject
Promise有三種狀態,分別是pending(等待中)、fulfilled(成功)、rejected(失敗)。狀態改變只能從pending => fulfilled,或者pending => rejected。
resolve的作用,就是將Promise的狀態從pending改為fulfilled,它接收一個參數作為Promise執行成功的值,這個值會傳給then的第一個回調函數。reject的作用是將Promise的狀態從pending改為rejected,它也接收一個參數作為Promise執行失敗的值,這個值會傳給then的第二個回調函數。
那么我們定義好狀態_status、_resolve、_reject,再定義兩個數組_handleFulfilled、_handleRejected,分別存放then的成功和失敗回調集合。當用戶調用resolve或reject方法后,開始異步調用_handleFulfilled或_handleRejected數組中的回調。
class MyPromise {
constructor(executor) {
if (!this._isFunction(executor)) {
throw new Error(`${executor} is not a function`);
}
this._status = "pending";
this._value = undefined;
this._handleFulfilled = [];
this._handleRejected = [];
// 很多文章在這里給executor加了try catch,實際上原生Promise的executor中的錯誤並沒有捕獲
executor(this._resolve.bind(this), this._reject.bind(this));
}
_isFunction(val) {
return Object.prototype.toString.call(val) === "[object Function]";
}
_resolve(value) {
if(this._status === 'pending'){
this._status = "fulfilled";
this._value = value;
let cb;
// 異步按順序調用並清空回調
setTimeout(() => {
while(cb = this._handleFulfilled.shift()){
cb(value);
}
}, 0)
}
}
_reject(value) {
if(this._status === 'pending'){
this._status = "rejected";
this._value = value;
let cb;
// 異步按順序調用並清空回調
setTimeout(() => {
while ((cb = this._handleRejected.shift())) {
cb(value);
}
}, 0);
}
}
}
Promise.then
Promise.then定義了兩個回調onFulfilled和onRejected
promise.then(onFulfilled, onRejected)
它們分別在Promise執行成功/失敗時執行,它們都是可選的,Promises/A+規范規定,如果onFulfilled或onRejected不是函數,將被忽略,Promise會繼續執行下一個then的回調。比如下面的例子會輸出1,.then(2)則被忽略了。
new Promise((resolve, reject) => {
resolve(1);
})
.then(2)
.then((res) => {
console.log(res);
});
then可以鏈式調用,是因為每個then都會返回一個新的Promise。為什么要返回新的Promise?因為每個then都可以返回不同的值,如果用同一個Promise,狀態變化后是不能再修改的,無法做到返回不同的值。
then的回調執行onFulfilled還是onRejected,取決於Promise的狀態,如果Promise狀態為pending,只會將onFulfilled和onRejected分別push到_handleFulfilled和_handleRejected數組;如果狀態為fulfilled,會執行對應的onFulfilled;如果狀態是rejected,執行對應的onRejected;
那么then方法的基本結構如下
then(onFulfilled, onRejected) {
const self = this;
const { _value, _status } = this;
// 如果onFulfilled、onRejected不是函數,強制改為函數,並且該函數直接返回接收到的參數,傳后面的then的回調函數
onFulfilled = self._isFunction(onFulfilled) ? onFulfilled : (v) => v;
onRejected = self._isFunction(onRejected) ? onRejected : (v) => v;
return new MyPromise((resolve, reject) => {
switch (_status) {
case "pending":
self._handleFulfilled.push(onFulfilled);
self._handleRejected.push(onRejected);
break;
case "fulfilled":
onFulfilled(_value);
// todo
break;
case "rejected":
onRejected(_value);
// todo
break;
default:
throw new Error('Promise resolver Unverified status');
break;
}
});
}
在then鏈式調用的情況下,如果前一個then返回的是一個新Promise,后一個then的回調必須等這個新Promise的狀態改變后才會執行。舉例,下面的代碼輸出1之后,等待3秒才會輸出2:
new Promise(resolve => {
resolve()
}).then(() => {
return new Promise(resolve => {
console.log(1);
setTimeout(() => {
resolve()
}, 3000)
})
}).then(() => {
console.log(2);
})
因此要對then的回調函數的返回值做個判斷,如果返回值不是Promise,利用resolve直接返回這個值;如果返回值是Promise,就要等這個Promise狀態變化之后再返回,而Promise狀態變化之后一定會調用then的回調函數,利用這個特性,將resolve、reject作為then的回調函數即可。
then(onFulfilled, onRejected) {
const self = this;
const { _value, _status } = this;
// 如果onFulfilled、onRejected不是函數,強制改為函數,並且該函數直接返回接收到的參數,傳后面的then的回調函數
onFulfilled = self._isFunction(onFulfilled) ? onFulfilled : (v) => v;
onRejected = self._isFunction(onRejected) ? onRejected : (v) => v;
return new MyPromise((resolve, reject) => {
const fulfilled = (value) => {
const res = onFulfilled(value);
if (res instanceof MyPromise) {
res.then(resolve, reject);
} else {
resolve(res);
}
};
const rejected = (value) => {
const res = onRejected(value);
if (res instanceof MyPromise) {
// 這里是重點
res.then(resolve, reject);
} else {
// 注意這里是resolve(res),而不是reject(res)
resolve(res);
}
};
switch (_status) {
case "pending":
self._handleFulfilled.push(fulfilled);
self._handleRejected.push(rejected);
break;
case "fulfilled":
fulfilled(_value);
break;
case "rejected":
rejected(_value);
break;
default:
throw new Error('Promise resolver Unverified status');
}
});
}
完整代碼
class MyPromise {
constructor(executor) {
if (!this._isFunction(executor)) {
throw new Error(`${executor} is not a function`);
}
this._status = "pending";
this._value = undefined;
this._handleFulfilled = [];
this._handleRejected = [];
// 很多文章在這里給executor加了try catch,實際上原生Promise的executor中的錯誤並沒有捕獲
executor(this._resolve.bind(this), this._reject.bind(this));
}
_isFunction(val) {
return Object.prototype.toString.call(val) === "[object Function]";
}
_resolve(value) {
if (this._status === "pending") {
this._status = "fulfilled";
this._value = value;
let cb;
// 異步按順序調用並清空回調
setTimeout(() => {
while ((cb = this._handleFulfilled.shift())) {
cb(value);
}
}, 0);
}
}
_reject(value) {
if (this._status === "pending") {
this._status = "rejected";
this._value = value;
let cb;
// 異步按順序調用並清空回調
setTimeout(() => {
while ((cb = this._handleRejected.shift())) {
cb(value);
}
}, 0);
}
}
then(onFulfilled, onRejected) {
const self = this;
const { _value, _status } = this;
// 如果onFulfilled、onRejected不是函數,強制改為函數,並且該函數直接返回接收到的參數,傳后面的then的回調函數
onFulfilled = self._isFunction(onFulfilled) ? onFulfilled : (v) => v;
onRejected = self._isFunction(onRejected) ? onRejected : (v) => v;
return new MyPromise((resolve, reject) => {
const fulfilled = (value) => {
const res = onFulfilled(value);
if (res instanceof MyPromise) {
res.then(resolve, reject);
} else {
resolve(res);
}
};
const rejected = (value) => {
const res = onRejected(value);
if (res instanceof MyPromise) {
// 這里是重點
res.then(resolve, reject);
} else {
// 注意這里是resolve(res),而不是reject(res)
resolve(res);
}
};
switch (_status) {
case "pending":
self._handleFulfilled.push(fulfilled);
self._handleRejected.push(rejected);
break;
case "fulfilled":
fulfilled(_value);
break;
case "rejected":
rejected(_value);
break;
default:
throw new Error('Promise resolver Unverified status');
}
});
}
}
測試一下,先輸出1,3秒后輸出2,說明MyPromise的基本功能沒問題了。
new MyPromise((resolve) => {
console.log(1);
setTimeout(() => {
resolve(2);
}, 3000)
}).then(res => {
console.log(res);
})
最后,總結一下,Promise是如何實現異步編程的?
Promise接收一個函數為參數,傳入了兩個內部的方法resolve和reject,然后用then注冊回調函數,手動調用resolve或reject就可以依次執行then的回調,並且給回調函數傳值。如果then返回的也是Promise,同樣的,手動調用resolve或reject后,才會繼續往下執行。
其實本質上還是回調函數,只不過寫法變了。
本文GitHub鏈接:Promise是如何實現異步編程的?