說明
由於手寫實現Promise無法區別宏任務與微任務,因此這裏的實現源碼只是爲了理解。
Promise規範要點
- Promise是一個類,接收一個函數作爲參數,該函數稱爲執行器,創建Promise對象時,執行器會立刻執行;
- Promise有三種狀態,分別爲pending等待、fulfilled成功、rejected失敗;其中pending會轉換爲fulfilled或者rejected,一旦狀態更新,則無法再更新狀態;
- 執行器函數接收兩個參數,分別爲resolve函數和reject函數,只是用於更新Promise對象的狀態,並可以將數據傳遞下去;reject的參數並不是錯誤,只有用throw語句纔會拋出錯誤。拋出錯誤的時候,由執行器函數或回調函數內部的try-catch語句來捕獲。由於我們的reject函數放在了catch語句裏,因此then方法的rejected回調函數使用throw來向下傳遞數據。
- then()方法內部需要判斷狀態,狀態成功則調用onFulfilled函數,狀態失敗則調用onRejected函數。
- 異步情況的處理,需要存儲then方法內部的回調函數,在異步任務結束時,調用resolve或reject後,同時調用相應的回調函數
// 定義狀態常量,在編輯器中可以得到代碼提示信息;
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
constructor(executor) {
// try-catch捕獲執行器異常
try {
executor(this.resolve, this.reject);
} catch(e) {
this.reject(e);
}
}
// 定義實例屬性,用於表示Promise的狀態
_status = PENDING;
// 成功之後的值
_value = undefined;
// 失敗之後的值
_err = undefined;
// 用於存儲成功回調函數的隊列,實現給同一個Promise用then()註冊多個回調函數
onSuccess = [];
// 用於存儲失敗回調函數的隊列,實現給同一個Promise用then()註冊多個回調函數
onFail = [];
// 之所以定義爲箭頭函數,是爲了在使用resolve()調用時,內部的this指向MyPromise的實例
resolve = (value) => {
// 判斷狀態,如果不是PENDING,則返回;
if(this._status !== PENDING) return;
// 更新狀態
this._status = FULFILLED;
// 保存value值
this._value = value;
// 判斷成功回調是否存在,如果存在,則調用
// this.onSuccess && this.onSuccess(this._value);
while(this.onSuccess.length) {
this.onSuccess.shift()();
}
}
reject = (err) => {
// 判斷狀態,如果不是PENDING,則返回;
if(this._status !== PENDING) return;
// 更新狀態
this._status = REJECTED;
// 保存err
this._err = err;
// 判斷失敗回調是否存在,如果存在,則調用
// this.onFail && this.onFail(this._err);
while(this.onFail.length) {
this.onFail.shift()();
}
}
// 實現then方法
then(successCallback, failCallback) {
// 解決then()方法可選參數的問題;
successCallback = successCallback ? successCallback: value => value;
// 用throw語句將異常傳遞下去,throw語句用來拋出異常,可以將throw關鍵字後面表達式的值拋出
failCallback = failCallback ? failCallback: err => { throw err; }
// 創建一個Promise對象並返回,實現鏈式調用;
let my_promise = new MyPromise((resolve, reject) => {
// 判斷狀態
if(this._status === FULFILLED) {
setTimeout(() => {
// 捕獲回調函數中的異常
try {
// 創建變量來接收成功回調函數的返回值;
let x = successCallback(this._value);
// 判斷x值是否是Promise對象,如果是普通值,則直接resolve或reject
// 否則需要查看Promise對象的值,再調用resolve或reject
// 調用resolvePromise(),將x的值傳遞出去;
// 由於三種狀態下都需要用到這個函數,所以這裏的resolvePromise()方法抽象爲公共函數
// 傳入my_promise是爲了對比my_promise與x是否相同,相同則表示出現Promise循環調用,需要拋出異常
// my_promise對象無法在同步的情況下拿到
// 因此將整個代碼放到setTimeout()中來獲取my_promise對象
resolvePromise(my_promise, x, resolve, reject);
} catch(e) {
reject(e);
}
}, 0);
} else if(this._status === REJECTED) {
setTimeout(() => {
try {
let x = failCallback(this._err);
resolvePromise(my_promise, x, resolve, reject);
} catch(e) {
reject(e);
}
}, 0);
} else {
// 處理異步情況,此時爲PENDING狀態,就將成功狀態和失敗狀態的回調函數存儲起來
this.onSuccess.push(() => {
setTimeout(() => {
try {
let x = successCallback(this._value);
} catch(e) {
reject(e);
}
}, 0);
});
this.onFail.push(() => {
setTimeout(() => {
try {
let x = failCallback(this._value);
} catch(e) {
reject(e);
}
}, 0);
});
}
});
return my_promise;
}
// 定義靜態方法all()
static all(arr) {
// 聲明一個數組,用來存儲arr中所有操作的結果
let result = [];
// 聲明一個變量index,用來判斷是否所有異步操作都已完成,如果異步操作未完成,index值小於arr.length
let index = 0;
return new MyPromise((resolve, reject) => {
// 聲明addData函數,用於將操作的結果存儲到result數組
function addData(key, value) {
result[key] = value;
index ++;
if(index === arr.length) {
resolve(result);
}
}
for(let i = 0; i < arr.length; i ++) {
let current = arr[i];
if(current instanceof MyPromise) {
// 當前arr元素爲Promise對象
current.then((value) => {
addData(value);
}, (err) => {
reject(err);
})
} else {
// 當前arr元素不是Promise對象
addData(i, arr[i]);
}
}
})
}
// 定義靜態方法resolve
static resolve(value) {
// 如果傳入的value是Promise對象,則原樣返回,否則返回新的Promise對象,將value傳遞
if(value instanceof MyPromise) {
return value;
} else {
return new MyPromise((resolve) => {
resolve(value);
})
}
}
// 定義實例finally方法,不管Promise對象狀態如何,finally的回調函數一定會執行
finally(callback) {
// 返回一個Promise對象
return this.then((value) => {
return MyPromise.resolve(callback()).then(() => value);
}, (err) => {
return MyPromise.resolve(callback()).then(() => { throw err; });
})
}
// 定義實例catch方法
catch(failCallback) {
// 調用then方法,只傳遞onRejected狀態的回調即可
return this.then(undefined, failCallback);
}
}
function resolvePromise(my_promise, x, resolve, reject) {
if(my_promise === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
}
if(x instanceof MyPromise) {
// x是Promise對象,則調用then方法查看值,並利用resolve或reject傳遞
/* x.then((value) => {
resolve(value)
}, (err) => {
reject(err);
}); */
x.then(resolve, reject);
} else {
resolve(x);
}
}