前言
關(guān)于動機(jī),無論是在工作還是面試中,都會遇到Promise的相關(guān)使用和原理,手寫Promise也有助于學(xué)習(xí)設(shè)計模式以及代碼設(shè)計。
本文主要介紹了如何使用自己的代碼去實(shí)現(xiàn)Promise的一些功能。
以下是本篇文章正文內(nèi)容
一、前置知識
手寫之前,需要知道promise有哪些基本特性:
1、promise有三種狀態(tài):pending、fulfilled、rejected。
2、promise的默認(rèn)狀態(tài)是pending,狀態(tài)流轉(zhuǎn)有兩種:pending—>fulfilled、pending—>rejected。
3、實(shí)例化Promise時,可以接收一個函數(shù)作為參數(shù),這個函數(shù)有兩個參數(shù),分別是resolve、reject方法用于改變實(shí)例狀態(tài)。并能夠執(zhí)行下一個then中的回調(diào)函數(shù)。
4、then方法中接收兩個函數(shù)作為參數(shù),第一個函數(shù)相當(dāng)于執(zhí)行上一個 promise 中執(zhí)行 resolve 后對應(yīng)的回調(diào),第二個函數(shù)相當(dāng)于執(zhí)行上一個 promise 中執(zhí)行 reject 后對應(yīng)的回調(diào)。
// 1. 實(shí)例化 Promise 時
// 可以接收一個函數(shù)作為參數(shù),這個函數(shù)可以接收到 resolve 和 reject 兩個實(shí)例方法
// 用于更改當(dāng)前實(shí)例的狀態(tài),并把它們接收到的參數(shù)傳遞給下一個 then 對應(yīng)的參數(shù)中
new Promise((resolve, reject) => {
// resolve 和 reject 可以都執(zhí)行,但都執(zhí)行的意義不大,因?yàn)?promise 狀態(tài)發(fā)生更改后,就不能在被更改
resolve('ok');
// reject('err');
}).then((value) => {
console.log("resolve callback = ", value); // 若執(zhí)行 resolve,則 value = ok
}, (reason) => {
console.log("reject callback = ", reason); // 若執(zhí)行 reject,則 value = err
});
5、then操作后會返回一個新的Promise,且支持鏈?zhǔn)秸{(diào)用。
let promise = new Promise((resolve,reject) => {
resolve(11)
})
const a = new Promise((resolve,reject) => {
reject('ok')
})
promise.then(res => {
console.log(res)
return a
}).then(res => console.log(res))
6、Promise.all以數(shù)組的形式接收多個Promise,當(dāng)所有Promise執(zhí)行完成,且狀態(tài)都為fulfilled時,返回執(zhí)行成功的結(jié)果;有一個失敗,則返回失敗的結(jié)果。
7、Promise.race以數(shù)組的形式接收多個Promise,只要有一個Promise先執(zhí)行成功,無論什么狀態(tài),都返回這個結(jié)果,給到下一個then中對應(yīng)的回調(diào)。
二、實(shí)現(xiàn)基本功能
1、
- new 操作
- resolve、reject方法
- then方法
// 定義三種狀態(tài)
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';
class Promise {
constructor(executor) {
// 1、默認(rèn)狀態(tài) - PENDING
this.status = PENDING;
// 2、內(nèi)部維護(hù)的變量值
this.value = undefined;
this.reason = undefined;
try {
executor(this.resolve.bind(this), this.reject.bind(this)) // 實(shí)例化傳進(jìn)來的函數(shù)會立即執(zhí)行
} catch(err) {
this.reject(err)
}
}
resolve(value) {
if (this.status === PENDING) {
this.value = value;
this.status = FULFILLED;
}
}
reject(reason) {
if (this.status === PENDING) {
this.reason = reason;
this.status = REJECTED;
}
}
then(onFulfilled, onRejected) {
if (onFulfilled && this.status === FULFILLED) {
let res = onFulfilled(this.value)
this.resolvePromise(res, resolve, reject)
}
if (onRejected && this.status === REJECTED) {
let res = onRejected(this.reason)
this.resolvePromise(res, resolve, reject)
}
}
}
通過以下代碼自測:
new Promise((resolve,reject) => {
resolve(1)
}).then(res=> {
console.log(res) // 1
})
2、考慮異步的情況。
以上代碼都是同步的,Promise實(shí)例化時傳入了異步函數(shù)如setTimeout,在setTimeout中resolve,在下一個then的回調(diào)函數(shù)獲取異步函數(shù)的狀態(tài)呢?
我們可以使用回調(diào)隊(duì)列緩存起來,等待執(zhí)行:
// 定義三種狀態(tài)
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';
class Promise {
constructor(executor) {
// 1、默認(rèn)狀態(tài) - PENDING
this.status = PENDING;
// 2、內(nèi)部維護(hù)的變量值
this.value = undefined;
this.reason = undefined;
this.onResolveCallBack = [];// 緩存 onResolve
this.onRejectCallBack = [];// 緩存 onReject
try {
executor(this.resolve.bind(this), this.reject.bind(this)) // 實(shí)例化傳進(jìn)來的函數(shù)會立即執(zhí)行
} catch(err) {
this.reject(err)
}
}
resolve(value) {
if (this.status === PENDING) {
this.value = value;
this.status = FULFILLED;
// 遍歷調(diào)用 onResolveCallBack
this.onResolveCallBack.forEach(fn => fn());
}
}
reject(reason) {
if (this.status === PENDING) {
this.reason = reason;
this.status = REJECTED;
this.onResolveCallBack.forEach(fn => fn());
}
}
then(onFulfilled, onRejected) {
if (onFulfilled && this.status === FULFILLED) {
let res = onFulfilled(this.value)
this.resolvePromise(res, resolve, reject)
}
if (onRejected && this.status === REJECTED) {
let res = onRejected(this.reason)
this.resolvePromise(res, resolve, reject)
}
// 當(dāng)前 promise 狀態(tài)為 pending,把當(dāng)前的 onResolve & onReject 緩存起來
if (this.status === PENDING) {
this.onResolveCallBack.push(() => {
onResolve(this.value);
});
this.onRejectCallBack.push(() => {
onReject(this.value);
});
}
}
}
用以下代碼測試通過:
let p = new MyPromise((resolve, reject) => {
setTimeout(()=>{
resolve(1);
},1000);
}).then(value => {
console.log('then resolve = ', value);
}, reason => {
console.log('then reject = ', reason);
});
// then resolve = 1
二、實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用
then(onFulfilled, onRejected) {
return new Promise((resolve, reject) => { // 支持鏈?zhǔn)秸{(diào)用
if (onFulfilled && this.status === FULFILLED) {
let res = onFulfilled(this.value)
resolve(res);
}
if (onRejected && this.status === REJECTED) {
let res = onRejected(this.reason)
resolve(res);
}
if (this.status === PENDING) {
this.onResolvedCallbacks.push(() =>{
let res = onFulfilled(this.value)
resolve(res);
})
this.onRejectedCallbacks.push(() => {
let res = onRejected(this.reason)
resolve(res);
})
}
})
}
測試用例:
let promise = new Promise((resolve,reject) => {
resolve(1)
})
promise.then(res => {
return 2 // 返回普通對象
}).then(res => console.log(res)) // 2
如果在then中返回的是普通對象,上述代碼能滿足,如果返回的是一個Promise,則還需要補(bǔ)充:
then(onFulfilled, onRejected) {
return new Promise((resolve, reject) => { // 支持鏈?zhǔn)秸{(diào)用
if (onFulfilled && this.status === FULFILLED) {
let res = onFulfilled(this.value)
this.resolvePromise(res, resolve, reject)
}
if (onRejected && this.status === REJECTED) {
let res = onRejected(this.reason)
this.resolvePromise(res, resolve, reject)
}
if (this.status === PENDING) {
this.onResolvedCallbacks.push(() =>{
let res = onFulfilled(this.value)
this.resolvePromise(res, resolve, reject)
})
this.onRejectedCallbacks.push(() => {
let res = onRejected(this.reason)
this.resolvePromise(res, resolve, reject)
})
}
})
}
resolvePromise(res, resolve, reject) {
if(res instanceof Promise) { // 對then中返回Promise做處理
res.then(resolve, reject)
} else{
// 普通值
resolve(res)
}
}
測試用例:
let promise = new Promise((resolve,reject) => {
resolve(11)
})
const a = new Promise((resolve,reject) => {
reject('ok')
})
promise.then(res => {
return a;
}).then(res => console.log(res)) // ok
三、實(shí)現(xiàn)Promise.all
由上述前置知識可知,Promise.all是全部成功才返回成功的結(jié)果,有一個失敗就返回失敗的結(jié)果:
Promise.all = function(arr) {
return new Promise((resolve, reject) => {
if (!Array.isArray(arr)) {
return reject("參數(shù)必須為數(shù)組");
}
let successNum = 0; // 成功的Promise條數(shù)
let resultArray = [];
let totalNum = arr.length; // 總Promise數(shù)量
let isFail = false;
arr.forEach(item => {
item.then(res => {
successNum++;
resultArray.push(res);
if (successNum === totalNum) { // 說明是全部成功
return resolve(resultArray)
}
}, err => {
if (!isFail) reject(err)
isFail = true;
})
});
})
}
測試用例:
function wait500(input) {
return new Promise((resolve, reject) => {
setTimeout(()=> {
resolve(500)
}, 500)
})
}
function wait1000(input) {
return new Promise((resolve, reject) => {
setTimeout(()=> {
resolve(1000)
}, 1000)
})
}
// 全部執(zhí)行完成之后回調(diào)
Promise.all([wait1000(), wait500()]).then(result => {
console.log('all end', result)
}, err => {
console.log('fafaf', err)
}) // 'all end [500, 1000]'
四、實(shí)現(xiàn)Promise.race
由上述前置知識可知,Promise.race是有一個promise先執(zhí)行成功,無論什么狀態(tài),都返回這個結(jié)果:
Promise.race = function(arr) {
return new Promise((resolve, reject) => {
if (!Array.isArray(arr)) {
return reject("參數(shù)必須為數(shù)組");
}
arr.forEach(item => {
item.then(res => {
return resolve(res); // 狀態(tài)不可逆,只要其中一個Promise轉(zhuǎn)變了狀態(tài),后續(xù)狀態(tài)就不會發(fā)生改變
}, err => {
reject(err);
})
});
})
}
測試用例:文章來源:http://www.zghlxwxcb.cn/news/detail-681995.html
function wait500(input) {
return new Promise((resolve, reject) => {
setTimeout(()=> {
resolve(500)
}, 500)
})
}
function wait1000(input) {
return new Promise((resolve, reject) => {
setTimeout(()=> {
resolve(1000)
}, 1000)
})
}
Promise.race([wait1000(), wait500()]).then(result => {
console.log('all end', result)
}, err => {
console.log('fafaf', err)
}) // 打印出'all end 500'
總結(jié)
總的代碼:文章來源地址http://www.zghlxwxcb.cn/news/detail-681995.html
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';
class Promise {
constructor(executor) {
// 1、默認(rèn)狀態(tài) - PENDING
this.status = PENDING;
// 2、內(nèi)部維護(hù)的變量值
this.value = undefined;
this.reason = undefined;
// 存放回調(diào)
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
try {
executor(this.resolve.bind(this), this.reject.bind(this))
} catch(err) {
this.reject(err)
}
}
resolve(value) {
if (this.status === PENDING) {
this.value = value;
this.status = FULFILLED;
this.onResolvedCallbacks.forEach(fn => fn())
}
}
reject(reason) {
if (this.status === PENDING) {
this.reason = reason;
this.status = REJECTED;
this.onRejectedCallbacks.forEach(fn => fn())
}
}
then(onFulfilled, onRejected) {
return new Promise((resolve, reject) => { // 支持鏈?zhǔn)秸{(diào)用
if (onFulfilled && this.status === FULFILLED) {
let res = onFulfilled(this.value)
this.resolvePromise(res, resolve, reject)
}
if (onRejected && this.status === REJECTED) {
let res = onRejected(this.reason)
this.resolvePromise(res, resolve, reject)
}
if (this.status === PENDING) {
this.onResolvedCallbacks.push(() =>{
let res = onFulfilled(this.value)
this.resolvePromise(res, resolve, reject)
})
this.onRejectedCallbacks.push(() => {
let res = onRejected(this.reason)
this.resolvePromise(res, resolve, reject)
})
}
})
}
resolvePromise(res, resolve, reject) {
if(res instanceof Promise) {
res.then(resolve, reject)
} else{
// 普通值
resolve(res)
}
}
}
// 全部成功才返回成功的結(jié)果,有一個失敗就返回失敗的結(jié)果
Promise.all = function(arr) {
return new Promise((resolve, reject) => {
if (!Array.isArray(arr)) {
return reject("參數(shù)必須為數(shù)組");
}
let successNum = 0;
let resultArray = [];
let totalNum = arr.length;
let isFail = false;
arr.forEach(item => {
item.then(res => {
successNum++;
resultArray.push(res);
if (successNum === totalNum) {
return resolve(resultArray)
}
}, err => {
if (!isFail) reject(err)
isFail = true;
})
});
})
}
// 某個promise先完成,
Promise.race = function(arr) {
return new Promise((resolve, reject) => {
if (!Array.isArray(arr)) {
return reject("參數(shù)必須為數(shù)組");
}
arr.forEach(item => {
item.then(res => {
return resolve(res);
}, err => {
reject(err);
})
});
})
}
到了這里,關(guān)于【手寫promise——基本功能、鏈?zhǔn)秸{(diào)用、promise.all、promise.race】的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!