參考文章:《Promise,從入門到放棄》
為了方便比較與原裝的
Promise區(qū)別,手寫的Promise被命名為iPromise。
實(shí)現(xiàn)本體
- 首先,
Promise是一個(gè)類,接收一個(gè)函數(shù)executor作為構(gòu)造函數(shù),該函數(shù)接受兩個(gè)函數(shù)作為參數(shù):resolve和reject(Promise自帶,并不需要使用者手動(dòng)部署),且立即(同步)執(zhí)行。
Promise對(duì)象的promiseResult屬性存儲(chǔ)執(zhí)行的結(jié)果,promiseState屬性存儲(chǔ)狀態(tài)。
Promise有三種狀態(tài):pending(準(zhǔn)備中)、fulfilled(滿足)和rejected(拒絕)。初始狀態(tài)是pending。
class iPromise {
constructor(executor) {
// 存儲(chǔ)promise結(jié)果
this.promiseResult = undefined;
// 存儲(chǔ)promise的狀態(tài)
this.promiseState = 'pending';
// 立即執(zhí)行executor
executor(resolve, reject);
}
}
-
resolve方法的作用是將執(zhí)行所得的結(jié)果賦值給promiseResult,并將promiseState由pending變?yōu)?code>fulfilled。reject方法基本一樣,只是將promiseState由pending變?yōu)?code>rejected。
promise對(duì)象的狀態(tài)變化只能變化一次;
executor執(zhí)行出現(xiàn)錯(cuò)誤,也會(huì)觸發(fā)reject函數(shù)。
class iPromise {
constructor(executor) {
// 存儲(chǔ)promise結(jié)果
this.promiseResult = undefined;
// 存儲(chǔ)promise的狀態(tài)
this.promiseState = 'pending';
// resolve方法,將promiseState變?yōu)閒ulfilled,并修改promiseResult
const resolve = (value) => {
// 僅在promiseState為pending的時(shí)候變化
if (this.promiseState !== 'pending') return;
// 將promiseState變?yōu)閒ulfilled
this.promiseState = 'fulfilled';
// 將value作為promiseResult
this.promiseResult = value;
}
// reject方法,將promiseState變?yōu)閞ejected,并修改promiseResult
const reject = (error) => {
// 僅在promiseState為pending的時(shí)候變化
if (this.promiseState !== 'pending') return;
// 將promiseState變?yōu)閞ejected
this.promiseState = 'rejected';
// 將error作為promiseResult
this.promiseResult = error;
}
// 立即執(zhí)行executor
// executor函數(shù)執(zhí)行出現(xiàn)錯(cuò)誤,會(huì)調(diào)用reject方法
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
}
實(shí)現(xiàn)then方法
-
Promise.then()方法接收1~2個(gè)回調(diào)函數(shù)作為參數(shù),返回一個(gè)新的Promise對(duì)象(下文中如果沒有特殊說明,Promise對(duì)象都指原Promise對(duì)象)以支持鏈?zhǔn)秸{(diào)用。
返回的新
Promise對(duì)象的參數(shù)必須使用箭頭函數(shù)()=>{},否則會(huì)造成this指向錯(cuò)誤的問題。當(dāng)然,你也可以采取let self = this;存儲(chǔ)this然后傳入的形式,不過有點(diǎn)多此一舉。
class iPromise {
constructor(executor){
// 構(gòu)造函數(shù)
}
// 接收兩個(gè)回調(diào)函數(shù)作為參數(shù)
then(onResolved, onRejected) {
/*
* 這里必須要寫箭頭函數(shù),否則this會(huì)指向新的Promise對(duì)象
* 進(jìn)而導(dǎo)致取不到promiseState和promiseResult
*/
return new iPromise((resolve, reject) => {
// ...
})
}
}
- 根據(jù)
promiseResult的值不同,分為兩種情況:當(dāng)其為Promise對(duì)象時(shí),遞歸執(zhí)行它的then方法;當(dāng)其為其他類型,直接調(diào)用resolve方法。
class iPromise {
constructor(executor){
// ...
}
// 接收兩個(gè)回調(diào)函數(shù)作為參數(shù)
then(onResolved, onRejected) {
/*
* 這里必須要寫箭頭函數(shù),否則this會(huì)指向新的Promise對(duì)象
* 進(jìn)而導(dǎo)致取不到promiseState和promiseResult
*/
return new iPromise((resolve, reject) => {
/*
* 回調(diào)處理函數(shù)
* 這里也請記得用箭頭函數(shù),this要穿透幾層
* 箭頭函數(shù)就用幾層
*/
const handleCallback = (callback) => {
try {
let res = callback(this.promiseResult);
// 若返回值是promise對(duì)象
if (res instanceof Promise) {
res.then(val => resolve(val), err => reject(err));
} else {
// 若不是
resolve(res);
}
} catch (error) {
reject(error);
}
}
})
}
}
- 根據(jù)
promiseState的值來確定應(yīng)該執(zhí)行哪個(gè)回調(diào)函數(shù):
class iPromise {
constructor(executor){
// ...
}
// 接收兩個(gè)回調(diào)函數(shù)作為參數(shù)
then(onResolved, onRejected) {
/*
* 這里必須要寫箭頭函數(shù),否則this會(huì)指向新的Promise對(duì)象
* 進(jìn)而導(dǎo)致取不到promiseState和promiseResult
*/
return new iPromise((resolve, reject) => {
/*
* 回調(diào)處理函數(shù)
* 這里也請記得用箭頭函數(shù),this要穿透幾層
* 箭頭函數(shù)就用幾層
*/
const handleCallback = (callback) => {
try {
let res = callback(this.promiseResult);
// 若返回值是promise對(duì)象
if (res instanceof Promise) {
res.then(val => resolve(val), err => reject(err));
} else {
// 若不是
resolve(res);
}
} catch (error) {
reject(error);
}
}
// promiseState為fulfilled時(shí)調(diào)用onResolved
if (this.promiseState === "fulfilled") {
handleCallback(onResolved);
}
// promiseState為rejected時(shí)調(diào)用onRejected
if (this.promiseState === "rejected") {
handleCallback(onRejected);
}
})
}
}
- 因?yàn)楫惒饺蝿?wù)的問題,并且支持多個(gè)回調(diào),所以我們需要對(duì)回調(diào)函數(shù)采用數(shù)組進(jìn)行存儲(chǔ),所以引入了新的變量:
callbackList:
class iPromise {
constructor(executor) {
// 存儲(chǔ)promise結(jié)果
this.promiseResult = undefined;
// 存儲(chǔ)promise的狀態(tài)
this.promiseState = 'pending';
// 存儲(chǔ)所有的回調(diào)函數(shù)
this.callbackList = [];
// resolve方法,將promiseState變?yōu)閒ulfilled,并修改promiseResult
const resolve = (value) => {
// 僅在promiseState為pending的時(shí)候變化
if (this.promiseState !== 'pending') return;
// 將promiseState變?yōu)閒ulfilled
this.promiseState = 'fulfilled';
// 將value作為promiseResult
this.promiseResult = value;
// 異步執(zhí)行所有回調(diào)函數(shù)
this.callbackList.forEach(cb => cb.onResolved(value));
}
// reject方法,將promiseState變?yōu)閞ejected,并修改promiseResult
const reject = (error) => {
// 僅在promiseState為pending的時(shí)候變化
if (this.promiseState !== 'pending') return;
// 將promiseState變?yōu)閞ejected
this.promiseState = 'rejected';
// 將error作為promiseResult
this.promiseResult = error;
// 異步執(zhí)行所有回調(diào)函數(shù)
this.callbackList.forEach(cb => cb.onRejected(error));
}
// 立即執(zhí)行executor
// executor函數(shù)執(zhí)行出現(xiàn)錯(cuò)誤,會(huì)調(diào)用reject方法
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
// 接收兩個(gè)回調(diào)函數(shù)作為參數(shù)
then(onResolved, onRejected) {
/*
* 這里必須要寫箭頭函數(shù),否則this會(huì)指向新的Promise對(duì)象
* 進(jìn)而導(dǎo)致取不到promiseState和promiseResult
*/
return new iPromise((resolve, reject) => {
/*
* 回調(diào)處理函數(shù)
* 這里也請記得用箭頭函數(shù),this要穿透幾層
* 箭頭函數(shù)就用幾層
*/
const handleCallback = (callback) => {
try {
let res = callback(this.promiseResult);
// 若返回值是promise對(duì)象
if (res instanceof Promise) {
res.then(val => resolve(val), err => reject(err));
} else {
// 若不是
resolve(res);
}
} catch (error) {
reject(error);
}
}
// promiseState為fulfilled時(shí)調(diào)用onResolved
if (this.promiseState === "fulfilled") {
handleCallback(onResolved);
}
// promiseState為rejected時(shí)調(diào)用onRejected
if (this.promiseState === "rejected") {
handleCallback(onRejected);
}
/*
* 如果是pending狀態(tài),則異步任務(wù),在改變狀態(tài)的時(shí)候去調(diào)用回調(diào)函數(shù)
* 所以要保存回調(diào)函數(shù)
* 因?yàn)閜romise實(shí)例可以指定多個(gè)回調(diào),于是采用數(shù)組
*/
if (this.promiseState === "pending") {
this.callbackList.push({
onResolved: () => {
handleCallback(onResolved)
},
onRejected: () => {
handleCallback(onRejected)
}
})
}
})
}
}
catch方法
catch方法主要需要做到的就是異常穿透:
當(dāng)使用
promise的then進(jìn)行鏈?zhǔn)秸{(diào)用時(shí),可以在最后指定失敗的回調(diào)。前面的任何錯(cuò)誤都會(huì)在最后傳到失敗的回調(diào)中去處理,除非在中途被失敗回調(diào)函數(shù)(onRejected)處理了。
// promise對(duì)象的異常穿透
let p1 = Promise.resolve(1);
p1.then((value)=>{
console.log(11);
}).then((value)=>{
throw 'err';
}).then((value)=>{
console.log(22);
}).catch(err=>{
console.log(err);
})
// 最終輸出:
// 11
// err
要實(shí)現(xiàn)catch,我們可以直接調(diào)用iPromise.then方法,但不傳入onResolve方法。
同時(shí),我們需要給then中的onResolve和onRejected賦初始值,順便避免了傳入undefined或其他非函數(shù)值而報(bào)錯(cuò):
class iPromise {
constructor(executor) {
// 構(gòu)造函數(shù)
}
// 接收兩個(gè)回調(diào)函數(shù)作為參數(shù)
then(onResolved, onRejected) {
// 處理異常穿透,并設(shè)置默認(rèn)值以避免程序出錯(cuò)
if(typeof onResolved !== 'function') {
onResolve = (val) => val;
}
if(typeof onRejected !== 'function') {
onRejected = (err) => {
throw err;
}
}
return new iPromise((resolve, reject) => {
// ...調(diào)用回調(diào)函數(shù)
})
}
// catch方法
catch(onRejected) {
return this.then(undefined, onRejected);
}
}
Promise.resolve方法
Promise.resolve方法返回成功或者失敗的Promise對(duì)象。如果傳入的參數(shù)為非Promise類型的對(duì)象,則返回的結(jié)果為成功的Promise對(duì)象。如果傳入的參數(shù)為Promise對(duì)象,則參數(shù)Promise返回的結(jié)果就是 Promise.resolve返回的結(jié)果。
let promiseA = Promise.resolve(1);
// 比如這時(shí)return 一個(gè)[[PromiseResult]]的值為err的Promise對(duì)象。
let PromiseB = Promise.resolve(new Promise((resolve,reject)=>{
reject('err');
})
實(shí)現(xiàn)它,我們需要用到靜態(tài)方法:
class iPromise {
constructor(executor) {
// 構(gòu)造函數(shù)
}
// ...其他方法
// 靜態(tài)方法只能通過類本身來調(diào)用
static resolve(value) {
return new iPromise((resolve, reject) => {
// 如果是iPromise對(duì)象
if (value instanceof iPromise) {
value.then(val => resolve(val), err => reject(err));
} else {
resolve(value);
}
})
}
}
Promise.reject方法
Promise.reject方法返回一個(gè)失敗的Promise對(duì)象,promiseResult的值為Promise.reject的參數(shù)。
有多失敗?大概像我一樣失敗。
let PromiseA = Promise.reject(new Promise((resolve,reject)=>{
resolve('err');
})
// 無論傳入是啥,就返回一個(gè)失敗的Promise對(duì)象,[[PromiseResult]]的值為 Promise.reject的參數(shù)
實(shí)現(xiàn):
class iPromise {
constructor(executor) {
// 構(gòu)造函數(shù)
}
// ...其他方法
// 靜態(tài)方法只能通過類本身來調(diào)用
static reject(error) {
return new iPromise((resolve, reject) => {
reject(error);
})
}
}
Promise.all方法
Promise.all方法接收的參數(shù)是由n個(gè)Promise對(duì)象的數(shù)組。返回新的Promise對(duì)象,只有所有的Promise對(duì)象都成功才成功,返回的對(duì)象的promiseResult為包含所有Promise對(duì)象的數(shù)組。只要有一個(gè)失敗了就直接失敗,返回的對(duì)象的promiseResult為失敗的Promise對(duì)象的執(zhí)行結(jié)果。
class iPromise {
constructor(executor) {
// 構(gòu)造函數(shù)
}
// ...其他方法
static all(promiseArrays) {
return new iPromise((resolve, reject) => {
// 用以存儲(chǔ)執(zhí)行的結(jié)果
let results = [];
let length = promiseArrays.length;
promiseArrays.forEach((promiseObj, index, promiseArrays) => {
promiseObj.then((val) => {
results.push(val);
// 由于是多個(gè)異步任務(wù)的關(guān)系,需要判斷是否都執(zhí)行完畢
if (results.length === length) {
resolve(results);
}
}, err => {
// 如有錯(cuò)誤,則reject
reject(err);
});
})
})
}
}
Promise.race方法
Promise.race方法接收的參數(shù)是由n個(gè)Promise對(duì)象的數(shù)組。返回新的Promise對(duì)象,第一個(gè)完成的Promise的結(jié)果狀態(tài)就是最終結(jié)果的狀態(tài)。
你可能會(huì)問:那不鐵定第一個(gè)Promise對(duì)象是第一個(gè)完成的嗎?
這是因?yàn)槲覀兊淖钪匾墓δ苓€沒做:異步。
class iPromise {
constructor(executor) {
// 構(gòu)造函數(shù)
}
// ...其他方法
// race方法
static race(promiseArrays) {
return new iPromise((resolve, reject) => {
promiseArrays.forEach(promiseObj => {
promiseObj.then(val => {
resolve(val);
}, err => {
reject(err);
});
})
})
}
}
加點(diǎn)細(xì)節(jié)—由同步到異步
使用setTimeout將其變?yōu)楫惒饺蝿?wù)。
setTimeout只能將任務(wù)變更為宏觀異步任務(wù)。原裝的Promise是微觀異步任務(wù)。
class iPromise {
constructor(executor) {
// 存儲(chǔ)promise結(jié)果
this.promiseResult = undefined;
// 存儲(chǔ)promise的狀態(tài)
this.promiseState = 'pending';
// 存儲(chǔ)所有的回調(diào)函數(shù)
this.callbackList = [];
// resolve方法,將promiseState變?yōu)閒ulfilled,并修改promiseResult
const resolve = (value) => {
// 僅在promiseState為pending的時(shí)候變化
if (this.promiseState !== 'pending') return;
// 將promiseState變?yōu)閒ulfilled
this.promiseState = 'fulfilled';
// 將value作為promiseResult
this.promiseResult = value;
// 異步執(zhí)行所有回調(diào)函數(shù)
setTimeout(()=>{
this.callbackList.forEach(cb => cb.onResolved(value));
})
}
// reject方法,將promiseState變?yōu)閞ejected,并修改promiseResult
const reject = (error) => {
// 僅在promiseState為pending的時(shí)候變化
if (this.promiseState !== 'pending') return;
// 將promiseState變?yōu)閞ejected
this.promiseState = 'rejected';
// 將error作為promiseResult
this.promiseResult = error;
// 異步執(zhí)行所有回調(diào)函數(shù)
setTimeout(()=>{
this.callbackList.forEach(cb => cb.onRejected(error));
})
}
// 立即執(zhí)行executor
// executor函數(shù)執(zhí)行出現(xiàn)錯(cuò)誤,會(huì)調(diào)用reject方法
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
// 接收兩個(gè)回調(diào)函數(shù)作為參數(shù)
then(onResolved, onRejected) {
//處理異常穿透并且為onResolved,onRejected設(shè)置默認(rèn)值。因?yàn)檫@兩個(gè)參數(shù)可以都不傳
if (typeof onRejected !== 'function') {
onRejected = err => {
throw err;
}
}
if (typeof onResolved !== 'function') {
onResolved = val => val;
}
/*
* 這里必須要寫箭頭函數(shù),否則this會(huì)指向新的Promise對(duì)象
* 進(jìn)而導(dǎo)致取不到promiseState和promiseResult
*/
return new iPromise((resolve, reject) => {
/*
* 回調(diào)處理函數(shù)
* 這里也請記得用箭頭函數(shù),this要穿透幾層
* 箭頭函數(shù)就用幾層
*/
const handleCallback = (callback) => {
try {
let res = callback(this.promiseResult);
// 若返回值是promise對(duì)象
if (res instanceof iPromise) {
res.then(val => resolve(val), err => reject(err));
} else {
// 若不是
resolve(res);
}
} catch (error) {
reject(error);
}
}
// promiseState為fulfilled時(shí)調(diào)用onResolved
if (this.promiseState === "fulfilled") {
setTimeout(() => {
handleCallback(onResolved);
});
}
// promiseState為rejected時(shí)調(diào)用onRejected
if (this.promiseState === "rejected") {
setTimeout(() => {
handleCallback(onRejected);
});
}
/*
* 如果是pending狀態(tài),則異步任務(wù),在改變狀態(tài)的時(shí)候去調(diào)用回調(diào)函數(shù)
* 所以要保存回調(diào)函數(shù)
* 因?yàn)閜romise實(shí)例可以指定多個(gè)回調(diào),于是采用數(shù)組
*/
if (this.promiseState === "pending") {
this.callbackList.push({
onResolved: () => {
handleCallback(onResolved)
},
onRejected: () => {
handleCallback(onRejected)
}
})
}
})
}
catch(onRejected) {
return this.then(undefined, onRejected);
}
static resolve(value) {
return new iPromise((resolve, reject) => {
if (value instanceof iPromise) {
value.then(val => resolve(val), err => reject(err));
} else {
resolve(value)
}
})
}
static reject(error) {
return new iPromise((resolve, reject) => {
reject(error);
})
}
static all(promiseArrays) {
return new iPromise((resolve, reject) => {
// 用以存儲(chǔ)執(zhí)行的結(jié)果
let results = [];
let length = promiseArrays.length;
promiseArrays.forEach((promiseObj, index, promiseArrays) => {
promiseObj.then((val) => {
results.push(val);
// 由于是多個(gè)異步任務(wù)的關(guān)系,需要判斷是否都執(zhí)行完畢
if (results.length === length) {
resolve(results);
}
}, err => {
// 如有錯(cuò)誤,則reject
reject(err);
});
})
})
}
static race(promiseArrays) {
return new iPromise((resolve, reject) => {
promiseArrays.forEach(promiseObj => {
promiseObj.then(val => {
resolve(val);
}, err => {
reject(err);
});
});
})
}
}
總結(jié)
......沒啥好說的,從上到下敲一遍,不說運(yùn)用自如,怎么也得是一臉懵逼吧。