Promise對象的特性
要實現(xiàn)Promise
對象首先我們要了解Promise
擁有哪些特性率挣,簡單概括為以下幾點
1绑榴、Promise
有三種狀態(tài):pending
(進行中)朵逝、fulfilled
(已成功)淳蔼、rejected
(已失敗)
2、Promise
對象接受一個回調(diào)函數(shù)作為參數(shù), 該回調(diào)函數(shù)接受兩個參數(shù)撤嫩,分別是成功時的回調(diào)resolve
和失敗時的回調(diào)reject
偎捎;另外resolve
的參數(shù)除了正常值以外, 還可能是一個Promise
對象的實例序攘;reject
的參數(shù)通常是一個Error
對象的實例茴她。
3、then
方法返回一個新的Promise
實例程奠,并接收兩個參數(shù)onResolved
(fulfilled
狀態(tài)的回調(diào))丈牢;
onRejected
(rejected
狀態(tài)的回調(diào),該參數(shù)可選)
4梦染、catch
方法返回一個新的Promise
實例
5赡麦、finally
方法不管Promise
狀態(tài)如何都會執(zhí)行,該方法的回調(diào)函數(shù)不接受任何參數(shù)
6帕识、Promise.all()
方法將多個多個Promise
實例泛粹,包裝成一個新的Promise
實例,該方法接受一個由Promise
對象
組成的數(shù)組作為參數(shù)(Promise.all()
方法的參數(shù)可以不是數(shù)組肮疗,但必須具有Iterator
接口晶姊,且返回的每個成員都是Promise
實例),注意參數(shù)中只要有一個實例觸發(fā)catch
方法伪货,都會觸發(fā)Promise.all()
方法返回的新的實例的catch
方法们衙,如果參數(shù)中的某個實例本身調(diào)用了catch
方法钾怔,將不會觸發(fā)Promise.all()
方法返回的新實例的catch
方法
7、Promise.race()
方法的參數(shù)與Promise.all
方法一樣蒙挑,參數(shù)中的實例只要有一個率先改變狀態(tài)就會將該實例的狀態(tài)傳給Promise.race()
方法宗侦,并將返回值作為Promise.race()
方法產(chǎn)生的Promise
實例的返回值
8、Promise.resolve()
將現(xiàn)有對象轉(zhuǎn)為Promise
對象忆蚀,如果該方法的參數(shù)為一個Promise
對象矾利,Promise.resolve()
將不做任何處理;如果參數(shù)thenable對象(即具有then方法)馋袜,Promise.resolve()
將該對象轉(zhuǎn)為Promise
對象并立即執(zhí)行then
方法男旗;如果參數(shù)是一個原始值,或者是一個不具有then
方法的對象欣鳖,則Promise.resolve
方法返回一個新的Promise
對象察皇,狀態(tài)為fulfilled
,其參數(shù)將會作為then
方法中onResolved
回調(diào)函數(shù)的參數(shù)泽台,如果Promise.resolve
方法不帶參數(shù)什荣,會直接返回一個fulfilled
狀態(tài)的 Promise
對象。需要注意的是师痕,立即resolve()的 Promise 對象溃睹,是在本輪“事件循環(huán)”(event loop)的結(jié)束時執(zhí)行而账,而不是在下一輪“事件循環(huán)”的開始時胰坟。
setTimeout(function () {
console.log('three');
}, 0);
Promise.resolve().then(function () {
console.log('two');
});
console.log('one');
// one
// two
// three
9、Promise.reject()
同樣返回一個新的Promise
對象泞辐,狀態(tài)為rejected
笔横,無論傳入任何參數(shù)都將作為reject()
的參數(shù)
以上就是Promise對象的一些基本特性,下面我們根據(jù)Promise
對象的特性咐吼,自己來實現(xiàn)一個簡單的Promise對象
第一步吹缔、Promise
對象用三種狀態(tài)分別是:pending、fulfilled锯茄、rejected厢塘。
當resolve
的參數(shù)為一個Promise
對象的實例的時時候該實例的resolve
的參數(shù)即為外層Promise對象then方法中onResolved方法的參數(shù),reject的參數(shù)即為外層Promise對象then方法(或catch方法)中onRejected方法的參數(shù)
function timeout2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
try {
a // 此處a未定義肌幽,如果注釋掉這里即正常執(zhí)行
resolve(200)
} catch (e) {
reject(e)
}
}, 1000)
})
}
function timeout(time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
try {
resolve(timeout2())
} catch (e) {
reject(e)
}
}, time)
})
}
let p = timeout(1000);
p.then(res => {
console.log(res); // 200
}).catch(err => {
console.log(err); // ReferenceError: a is not defined
})
由上面的例子我們可以定義一個基本框架:
/*
Promise構(gòu)造函數(shù)接收一個executor函數(shù)晚碾, executor函數(shù)執(zhí)行完同步或異步操作后,調(diào)用它的兩個參數(shù)resolve和reject
如果操作成功喂急,調(diào)用resolve并傳入value
如果操作失敗格嘁,調(diào)用reject并傳入reason
*/
class MyPromise {
constructor(executor) {
if(typeof executor !== 'function') {
throw new Error('MyPromise must accept a function as a parameter')
}
// Promise當前的狀態(tài)
this.status = 'pending'
// Promise的值
this.data = undefined
// Promise resolve時的回調(diào)函數(shù)集,因為在Promise結(jié)束之前有可能有多個回調(diào)添加到它上面
this.onResolvedCallback = []
// Promise reject時的回調(diào)函數(shù)集廊移,因為在Promise結(jié)束之前有可能有多個回調(diào)添加到它上面
this.onRejectedCallback = []
/*
考慮到執(zhí)行executor的過程中有可能出錯糕簿,所以我們用try/catch塊給包起來探入,
并且在出錯后以catch到的值reject掉這個Promise,另外因為resolve和reject在外部調(diào)用故需要綁定this
*/
try {
executor(this.resolve.bind(this), this.reject.bind(this))
} catch (err) {
this.reject(err)
}
}
resolve(value) {
// 成功時將狀態(tài)改為fulfilled
if(this.status === 'padding') {
// 如果傳入的值是一個promise實例,則必須等待該Promise對象狀態(tài)改變后,
// 當前Promsie的狀態(tài)才會改變懂诗,且狀態(tài)取決于參數(shù)Promsie對象的狀態(tài)
if(value instanceof MyPromise) {
value.then(res => {
this.data = res
this.status = 'fulfilled'
//執(zhí)行resolve的回調(diào)函數(shù)蜂嗽,將value傳遞到callback中
this.onResolvedCallback.forEach(callback => callback(res))
}, err => {
this.data = err
this.status = 'rejected'
//執(zhí)行reject的回調(diào)函數(shù),將reason傳遞到callback中
this.onRejectedCallback.forEach(callback => callback(err))
})
} else {
this.status = 'fulfilled';
this.data = value;
//執(zhí)行resolve的回調(diào)函數(shù)殃恒,將value傳遞到callback中
this.onResolvedCallback.forEach(callback => callback(value))
}
}
}
reject(reason) {
// 失敗時將狀態(tài)改為rejected
if(this.status === 'padding') {
this.status = 'rejected'
this.data = reason;
// 觸發(fā)所有的回調(diào)函數(shù)
this.onRejectedCallback.forEach(item => {
item(reason)
})
}
}
}
第二步徒爹、實現(xiàn)then方法
Promise
實例的then
方法返回一個新的Promise
實例,并接收兩個參數(shù)onResolved
(fulfilled狀態(tài)的回調(diào))芋类; onRejected
(rejected狀態(tài)的回調(diào)隆嗅,該參數(shù)可選);
// then方法接收兩個參數(shù),onResolved侯繁,onRejected胖喳,分別為Promise成功或失敗后的回調(diào)
class MyPromise {
// ....
then(onResolved, onRejected) {
// 根據(jù)標準,如果then的參數(shù)不是function贮竟,則我們需要忽略它丽焊,此處以如下方式處理
onResolved = typeof onResolved === 'function' ? onResolved : value => {}
onRejected = typeof onRejected === 'function' ? onRejected : reason => {}
if (this.status === 'fulfilled') {
return new MyPromise((resolve, reject) => {
})
}
if (this.status === 'rejected') {
return new MyPromise((resolve, reject) => {
})
}
if (this.status === 'pending') {
return new MyPromise((resolve, reject) => {
})
}
}
}
當我們在鏈式調(diào)用Promise
實例的時候,當一個實例的then方法返回另一個實例咕别,第二個then方法指定的回調(diào)函數(shù)技健,就會等待這個新的Promise對象狀態(tài)發(fā)生變化。如果變?yōu)閒ulfilled惰拱,就調(diào)用第一個回調(diào)函數(shù)(即onResolved)雌贱,如果狀態(tài)變?yōu)閞ejected,就調(diào)用第二個回調(diào)函數(shù)(即onRejected)偿短。
let p1 = new Promise((resolve,reject) => {
setTimeout(function(){
try {
resolve(1)
} catch (e) {
reject(e)
}
}, 100)
})
let p2 = new Promise((resolve,reject) => {
setTimeout(function(){
try {
resolve(2)
} catch (e) {
reject(e)
}
}, 100)
})
p1.then(res => {
console.log(res); // 1
return p2
}).then(res => {
console.log(res); // 2
}).catch(err => {
})
第三步欣孤、完善then方法
根據(jù)上面的例子我們來補充then方法里面的內(nèi)容
// then方法接收兩個參數(shù),onResolved昔逗,onRejected降传,分別為Promise成功或失敗后的回調(diào)
class MyPromise {
// ....
then(onResolved, onRejected) {
// 根據(jù)標準,如果then的參數(shù)不是function勾怒,則我們需要忽略它婆排,此處以如下方式處理
onResolved = typeof onResolved === 'function' ? onResolved : value => value
onRejected = typeof onRejected === 'function' ? onRejected : reason => reason
// 如果promise1(此處即為this)的狀態(tài)已經(jīng)確定并且是resolved,我們調(diào)用onResolved
// 因為考慮到有可能throw笔链,所以我們將其包在try/catch塊里
if (this.status === 'fulfilled') {
return new MyPromise((resolve, reject) => {
try {
let result = onResolved(this.data)
// 如果onResolved的返回值是一個Promise對象段只,直接取它的結(jié)果做為返回promise實例的結(jié)果
if(result instanceof MyPromise) {
result.then(resolve, reject)
}
resolve(result) // 否則,以它的返回值做為返回promise實例的結(jié)果
} catch (e) {
reject(e)
}
})
}
// rejected狀態(tài)的處理方法與上面基本一致
if (this.status === 'rejected') {
return new MyPromise((resolve, reject) => {
try {
let result = onRejected(this.data)
// 如果onRejected的返回值是一個Promise對象卡乾,直接取它的結(jié)果做為返回promise實例的結(jié)果
if(result instanceof MyPromise) {
result.then(resolve, reject)
}
} catch (e) {
reject(e)
}
})
}
if (this.status === 'pending') {
/**
* 如果當前的Promise還處于pending狀態(tài)翼悴,我們并不能確定調(diào)用onResolved還是onRejected,
* 只能等到Promise的狀態(tài)確定后,才能確實如何處理鹦赎。
* 所以我們需要把以上兩種情況的處理邏輯做為callback放入promise1(此處即this)的回調(diào)數(shù)組里
* 具體邏輯也與上面類似
*/
return new MyPromise((resolve, reject) => {
this.onResolvedCallback.push((value) => {
try {
let result = onResolved(this.data);
if (result instanceof MyPromise) {
result.then(resolve, reject)
}
} catch (e) {
reject(e)
}
})
this.onRejectedCallback.push(reason => {
try {
let result = onRejected(this.data)
if (result instanceof MyPromise) {
result.then(resolve, reject)
}
} catch (e) {
reject(e)
}
})
})
}
}
}
第四步谍椅、完成catch方法
以上我們就實現(xiàn)了一個promise對象的基本功能,最后加上catch方法完整代碼如下
/*
Promise構(gòu)造函數(shù)接收一個executor函數(shù)古话, executor函數(shù)執(zhí)行完同步或異步操作后雏吭,調(diào)用它的兩個參數(shù)resolve和reject
如果操作成功,調(diào)用resolve并傳入value
如果操作失敗陪踩,調(diào)用reject并傳入reason
*/
class MyPromise {
constructor(executor) {
if(typeof executor !== 'function') {
throw new Error('MyPromise must accept a function as a parameter')
}
// Promise當前的狀態(tài)
this.status = 'pending'
// Promise的值
this.data = undefined
// Promise resolve時的回調(diào)函數(shù)集杖们,因為在Promise結(jié)束之前有可能有多個回調(diào)添加到它上面
this.onResolvedCallback = []
// Promise reject時的回調(diào)函數(shù)集,因為在Promise結(jié)束之前有可能有多個回調(diào)添加到它上面
this.onRejectedCallback = []
/*
考慮到執(zhí)行executor的過程中有可能出錯肩狂,所以我們用try/catch塊給包起來摘完,
并且在出錯后以catch到的值reject掉這個Promise,另外因為resolve和reject在外部調(diào)用故需要綁定this
*/
try {
executor(this.resolve.bind(this), this.reject.bind(this))
} catch (err) {
this.reject(err)
}
}
resolve(value) {
// 成功時將狀態(tài)改為fulfilled
if(this.status === 'padding') {
// 如果傳入的值是一個promise實例,則必須等待該Promise對象狀態(tài)改變后,
// 當前Promsie的狀態(tài)才會改變傻谁,且狀態(tài)取決于參數(shù)Promsie對象的狀態(tài)
if(value instanceof MyPromise) {
value.then(res => {
this.data = res
this.status = 'fulfilled'
//執(zhí)行resolve的回調(diào)函數(shù)孝治,將value傳遞到callback中
this.onResolvedCallback.forEach(callback => callback(res))
}, err => {
this.data = err
this.status = 'rejected'
//執(zhí)行reject的回調(diào)函數(shù),將reason傳遞到callback中
this.onRejectedCallback.forEach(callback => callback(err))
})
} else {
this.status = 'fulfilled';
this.data = value;
//執(zhí)行resolve的回調(diào)函數(shù)审磁,將value傳遞到callback中
this.onResolvedCallback.forEach(callback => callback(value))
}
}
}
reject(reason) {
// 失敗時將狀態(tài)改為rejected
if(this.status === 'padding') {
this.status = 'rejected'
this.data = reason;
// 觸發(fā)所有的回調(diào)函數(shù)
this.onRejectedCallback.forEach(item => {
item(reason)
})
}
}
then(onResolved, onRejected) {
// 根據(jù)標準谈飒,如果then的參數(shù)不是function,則我們需要忽略它态蒂,此處以如下方式處理
onResolved = typeof onResolved === 'function' ? onResolved : value => value
onRejected = typeof onRejected === 'function' ? onRejected : reason => reason
// 如果promise1(此處即為this)的狀態(tài)已經(jīng)確定并且是resolved杭措,我們調(diào)用onResolved
// 因為考慮到有可能throw,所以我們將其包在try/catch塊里
if (this.status === 'fulfilled') {
return new MyPromise((resolve, reject) => {
try {
let result = onResolved(this.data)
// 如果onResolved的返回值是一個Promise對象钾恢,直接取它的結(jié)果做為返回promise實例的結(jié)果
if(result instanceof MyPromise) {
result.then(resolve, reject)
}
resolve(result) // 否則手素,以它的返回值做為返回promise實例的結(jié)果
} catch (e) {
reject(e)
}
})
}
// rejected狀態(tài)的處理方法與上面基本一致
if (this.status === 'rejected') {
return new MyPromise((resolve, reject) => {
try {
let result = onRejected(this.data)
// 如果onRejected的返回值是一個Promise對象,直接取它的結(jié)果做為返回promise實例的結(jié)果
if(result instanceof MyPromise) {
result.then(resolve, reject)
}
} catch (e) {
reject(e)
}
})
}
if (this.status === 'pending') {
/**
* 如果當前的Promise還處于pending狀態(tài)赘那,我們并不能確定調(diào)用onResolved還是onRejected刑桑,
* 只能等到Promise的狀態(tài)確定后,才能確實如何處理募舟。
* 所以我們需要把以上兩種情況的處理邏輯做為callback放入promise1(此處即this)的回調(diào)數(shù)組里
* 具體邏輯也與上面類似
*/
return new MyPromise((resolve, reject) => {
this.onResolvedCallback.push((value) => {
try {
let result = onResolved(this.data);
if (result instanceof MyPromise) {
result.then(resolve, reject)
}
} catch (e) {
reject(e)
}
})
this.onRejectedCallback.push(reason => {
try {
let result = onRejected(this.data)
if (result instanceof MyPromise) {
result.then(resolve, reject)
}
} catch (e) {
reject(e)
}
})
})
}
}
catch(onRejected) {
return this.then(null, onRejected)
}
}
以上就實現(xiàn)了Promise對象的核心功能,其他的幾個特性很容易實現(xiàn)在此不做贅述闻察,由于本人水平有限拱礁,如有錯漏之處請指出斧正。