參考文章:《Promise,從入門到放棄》
為了方便比較與原裝的
Promise
區(qū)別养铸,手寫的Promise
被命名為iPromise
宽气。
實現(xiàn)本體
- 首先婴洼,
Promise
是一個類个少,接收一個函數(shù)executor
作為構(gòu)造函數(shù),該函數(shù)接受兩個函數(shù)作為參數(shù):resolve
和reject
(Promise
自帶庆亡,并不需要使用者手動部署),且立即(同步)執(zhí)行捞稿。
Promise
對象的promiseResult
屬性存儲執(zhí)行的結(jié)果又谋,promiseState
屬性存儲狀態(tài)。
Promise
有三種狀態(tài):pending
(準(zhǔn)備中)娱局、fulfilled
(滿足)和rejected
(拒絕)彰亥。初始狀態(tài)是pending
。
class iPromise {
constructor(executor) {
// 存儲promise結(jié)果
this.promiseResult = undefined;
// 存儲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
對象的狀態(tài)變化只能變化一次废酷;
executor
執(zhí)行出現(xiàn)錯誤,也會觸發(fā)reject
函數(shù)犬第。
class iPromise {
constructor(executor) {
// 存儲promise結(jié)果
this.promiseResult = undefined;
// 存儲promise的狀態(tài)
this.promiseState = 'pending';
// resolve方法锦积,將promiseState變?yōu)閒ulfilled,并修改promiseResult
const resolve = (value) => {
// 僅在promiseState為pending的時候變化
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的時候變化
if (this.promiseState !== 'pending') return;
// 將promiseState變?yōu)閞ejected
this.promiseState = 'rejected';
// 將error作為promiseResult
this.promiseResult = error;
}
// 立即執(zhí)行executor
// executor函數(shù)執(zhí)行出現(xiàn)錯誤,會調(diào)用reject方法
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
}
實現(xiàn)then方法
-
Promise.then()
方法接收1~2個回調(diào)函數(shù)作為參數(shù)鉴分,返回一個新的Promise
對象(下文中如果沒有特殊說明哮幢,Promise
對象都指原Promise
對象)以支持鏈?zhǔn)秸{(diào)用。
返回的新
Promise
對象的參數(shù)必須使用箭頭函數(shù)()=>{}
志珍,否則會造成this
指向錯誤的問題橙垢。當(dāng)然,你也可以采取let self = this;
存儲this
然后傳入的形式伦糯,不過有點多此一舉柜某。
class iPromise {
constructor(executor){
// 構(gòu)造函數(shù)
}
// 接收兩個回調(diào)函數(shù)作為參數(shù)
then(onResolved, onRejected) {
/*
* 這里必須要寫箭頭函數(shù),否則this會指向新的Promise對象
* 進(jìn)而導(dǎo)致取不到promiseState和promiseResult
*/
return new iPromise((resolve, reject) => {
// ...
})
}
}
- 根據(jù)
promiseResult
的值不同敛纲,分為兩種情況:當(dāng)其為Promise
對象時喂击,遞歸執(zhí)行它的then
方法;當(dāng)其為其他類型淤翔,直接調(diào)用resolve
方法翰绊。
class iPromise {
constructor(executor){
// ...
}
// 接收兩個回調(diào)函數(shù)作為參數(shù)
then(onResolved, onRejected) {
/*
* 這里必須要寫箭頭函數(shù),否則this會指向新的Promise對象
* 進(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對象
if (res instanceof Promise) {
res.then(val => resolve(val), err => reject(err));
} else {
// 若不是
resolve(res);
}
} catch (error) {
reject(error);
}
}
})
}
}
- 根據(jù)
promiseState
的值來確定應(yīng)該執(zhí)行哪個回調(diào)函數(shù):
class iPromise {
constructor(executor){
// ...
}
// 接收兩個回調(diào)函數(shù)作為參數(shù)
then(onResolved, onRejected) {
/*
* 這里必須要寫箭頭函數(shù)监嗜,否則this會指向新的Promise對象
* 進(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對象
if (res instanceof Promise) {
res.then(val => resolve(val), err => reject(err));
} else {
// 若不是
resolve(res);
}
} catch (error) {
reject(error);
}
}
// promiseState為fulfilled時調(diào)用onResolved
if (this.promiseState === "fulfilled") {
handleCallback(onResolved);
}
// promiseState為rejected時調(diào)用onRejected
if (this.promiseState === "rejected") {
handleCallback(onRejected);
}
})
}
}
- 因為異步任務(wù)的問題,并且支持多個回調(diào)裁奇,所以我們需要對回調(diào)函數(shù)采用數(shù)組進(jìn)行存儲桐猬,所以引入了新的變量:
callbackList
:
class iPromise {
constructor(executor) {
// 存儲promise結(jié)果
this.promiseResult = undefined;
// 存儲promise的狀態(tài)
this.promiseState = 'pending';
// 存儲所有的回調(diào)函數(shù)
this.callbackList = [];
// resolve方法,將promiseState變?yōu)閒ulfilled框喳,并修改promiseResult
const resolve = (value) => {
// 僅在promiseState為pending的時候變化
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的時候變化
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)錯誤五垮,會調(diào)用reject方法
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
// 接收兩個回調(diào)函數(shù)作為參數(shù)
then(onResolved, onRejected) {
/*
* 這里必須要寫箭頭函數(shù)乍惊,否則this會指向新的Promise對象
* 進(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對象
if (res instanceof Promise) {
res.then(val => resolve(val), err => reject(err));
} else {
// 若不是
resolve(res);
}
} catch (error) {
reject(error);
}
}
// promiseState為fulfilled時調(diào)用onResolved
if (this.promiseState === "fulfilled") {
handleCallback(onResolved);
}
// promiseState為rejected時調(diào)用onRejected
if (this.promiseState === "rejected") {
handleCallback(onRejected);
}
/*
* 如果是pending狀態(tài)放仗,則異步任務(wù)润绎,在改變狀態(tài)的時候去調(diào)用回調(diào)函數(shù)
* 所以要保存回調(diào)函數(shù)
* 因為promise實例可以指定多個回調(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)用時诞挨,可以在最后指定失敗的回調(diào)莉撇。前面的任何錯誤都會在最后傳到失敗的回調(diào)中去處理,除非在中途被失敗回調(diào)函數(shù)(onRejected
)處理了惶傻。
// promise對象的異常穿透
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
要實現(xiàn)catch
棍郎,我們可以直接調(diào)用iPromise.then
方法,但不傳入onResolve
方法银室。
同時涂佃,我們需要給then
中的onResolve
和onRejected
賦初始值,順便避免了傳入undefined
或其他非函數(shù)值而報錯:
class iPromise {
constructor(executor) {
// 構(gòu)造函數(shù)
}
// 接收兩個回調(diào)函數(shù)作為參數(shù)
then(onResolved, onRejected) {
// 處理異常穿透蜈敢,并設(shè)置默認(rèn)值以避免程序出錯
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
對象辜荠。如果傳入的參數(shù)為非Promise
類型的對象,則返回的結(jié)果為成功的Promise
對象抓狭。如果傳入的參數(shù)為Promise
對象伯病,則參數(shù)Promise
返回的結(jié)果就是 Promise.resolve
返回的結(jié)果。
let promiseA = Promise.resolve(1);
// 比如這時return 一個[[PromiseResult]]的值為err的Promise對象否过。
let PromiseB = Promise.resolve(new Promise((resolve,reject)=>{
reject('err');
})
實現(xiàn)它午笛,我們需要用到靜態(tài)方法:
class iPromise {
constructor(executor) {
// 構(gòu)造函數(shù)
}
// ...其他方法
// 靜態(tài)方法只能通過類本身來調(diào)用
static resolve(value) {
return new iPromise((resolve, reject) => {
// 如果是iPromise對象
if (value instanceof iPromise) {
value.then(val => resolve(val), err => reject(err));
} else {
resolve(value);
}
})
}
}
Promise.reject方法
Promise.reject
方法返回一個失敗的Promise
對象,promiseResult
的值為Promise.reject
的參數(shù)苗桂。
有多失斠┗恰?大概像我一樣失敗誉察。
let PromiseA = Promise.reject(new Promise((resolve,reject)=>{
resolve('err');
})
// 無論傳入是啥与涡,就返回一個失敗的Promise對象惹谐,[[PromiseResult]]的值為 Promise.reject的參數(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個Promise
對象的數(shù)組持偏。返回新的Promise
對象驼卖,只有所有的Promise
對象都成功才成功,返回的對象的promiseResult
為包含所有Promise
對象的數(shù)組鸿秆。只要有一個失敗了就直接失敗酌畜,返回的對象的promiseResult
為失敗的Promise
對象的執(zhí)行結(jié)果。
class iPromise {
constructor(executor) {
// 構(gòu)造函數(shù)
}
// ...其他方法
static all(promiseArrays) {
return new iPromise((resolve, reject) => {
// 用以存儲執(zhí)行的結(jié)果
let results = [];
let length = promiseArrays.length;
promiseArrays.forEach((promiseObj, index, promiseArrays) => {
promiseObj.then((val) => {
results.push(val);
// 由于是多個異步任務(wù)的關(guān)系卿叽,需要判斷是否都執(zhí)行完畢
if (results.length === length) {
resolve(results);
}
}, err => {
// 如有錯誤桥胞,則reject
reject(err);
});
})
})
}
}
Promise.race方法
Promise.race方法接收的參數(shù)是由n個Promise對象的數(shù)組。返回新的Promise
對象考婴,第一個完成的Promise的結(jié)果狀態(tài)就是最終結(jié)果的狀態(tài)贩虾。
你可能會問:那不鐵定第一個Promise對象是第一個完成的嗎?
這是因為我們的最重要的功能還沒做:異步沥阱。
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);
});
})
})
}
}
加點細(xì)節(jié)—由同步到異步
使用setTimeout
將其變?yōu)楫惒饺蝿?wù)缎罢。
setTimeout
只能將任務(wù)變更為宏觀異步任務(wù)。原裝的Promise
是微觀異步任務(wù)考杉。
class iPromise {
constructor(executor) {
// 存儲promise結(jié)果
this.promiseResult = undefined;
// 存儲promise的狀態(tài)
this.promiseState = 'pending';
// 存儲所有的回調(diào)函數(shù)
this.callbackList = [];
// resolve方法策精,將promiseState變?yōu)閒ulfilled,并修改promiseResult
const resolve = (value) => {
// 僅在promiseState為pending的時候變化
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的時候變化
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)錯誤,會調(diào)用reject方法
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
// 接收兩個回調(diào)函數(shù)作為參數(shù)
then(onResolved, onRejected) {
//處理異常穿透并且為onResolved枕稀,onRejected設(shè)置默認(rèn)值询刹。因為這兩個參數(shù)可以都不傳
if (typeof onRejected !== 'function') {
onRejected = err => {
throw err;
}
}
if (typeof onResolved !== 'function') {
onResolved = val => val;
}
/*
* 這里必須要寫箭頭函數(shù),否則this會指向新的Promise對象
* 進(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對象
if (res instanceof iPromise) {
res.then(val => resolve(val), err => reject(err));
} else {
// 若不是
resolve(res);
}
} catch (error) {
reject(error);
}
}
// promiseState為fulfilled時調(diào)用onResolved
if (this.promiseState === "fulfilled") {
setTimeout(() => {
handleCallback(onResolved);
});
}
// promiseState為rejected時調(diào)用onRejected
if (this.promiseState === "rejected") {
setTimeout(() => {
handleCallback(onRejected);
});
}
/*
* 如果是pending狀態(tài)范抓,則異步任務(wù),在改變狀態(tài)的時候去調(diào)用回調(diào)函數(shù)
* 所以要保存回調(diào)函數(shù)
* 因為promise實例可以指定多個回調(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) => {
// 用以存儲執(zhí)行的結(jié)果
let results = [];
let length = promiseArrays.length;
promiseArrays.forEach((promiseObj, index, promiseArrays) => {
promiseObj.then((val) => {
results.push(val);
// 由于是多個異步任務(wù)的關(guān)系匕垫,需要判斷是否都執(zhí)行完畢
if (results.length === length) {
resolve(results);
}
}, err => {
// 如有錯誤,則reject
reject(err);
});
})
})
}
static race(promiseArrays) {
return new iPromise((resolve, reject) => {
promiseArrays.forEach(promiseObj => {
promiseObj.then(val => {
resolve(val);
}, err => {
reject(err);
});
});
})
}
}
總結(jié)
......沒啥好說的虐呻,從上到下敲一遍象泵,不說運用自如,怎么也得是一臉懵逼吧斟叼。