?? 個人主頁:不叫貓先生
???♂? 作者簡介:2022年度博客之星前端領(lǐng)域TOP 2,前端領(lǐng)域優(yōu)質(zhì)作者、阿里云專家博主,專注于前端各領(lǐng)域技術(shù),共同學(xué)習(xí)共同進步,一起加油呀!
??優(yōu)質(zhì)專欄:vue3從入門到精通、TypeScript從入門到實踐
?? 資料領(lǐng)?。呵岸诉M階資料可以找我免費領(lǐng)取
?? 摸魚學(xué)習(xí)交流:我們的宗旨是在工作中摸魚,摸魚中進步,期待大佬一起來摸魚(文末有我wx或者私信)。
面試中經(jīng)常會被問到你會手寫Promise嗎?本文帶你手?jǐn)]Promsie
基礎(chǔ)回顧
先回顧一下Promise的基本使用方法及特點
-
promise三個狀態(tài):進?中(pending)、已完成(fulfilled)、已拒絕(rejected)
-
處理promise異常的三種方式:
- 通過promise的then的第二個參數(shù)
- 通過.catch處理
- 通過try…catch處理
-
promise狀態(tài)處理
- 處于等待態(tài)時,promise 需滿?以下條件:可以變?yōu)椤敢淹瓿伞够颉敢丫芙^」
- 處于已完成時,promise 需滿?以下條件:不能遷移?其他任何狀態(tài);必須擁有?個不可變的值
- 處于已拒絕時,promise 需滿?以下條件:不能遷移?其他任何狀態(tài);必須擁有?個不可變的原
一、聲明Promise類,并進行初始化操作
首先定義一個Promise類,然后進行一些初始化操作。
- 接收一個回調(diào)函數(shù)callback,回調(diào)函數(shù)包含兩個參數(shù),一個resolve,一個reject
- 初始化狀態(tài)為pending
- 初始化成功狀態(tài)的值
- 初始化失敗狀態(tài)的值
- 定義resolve函數(shù)
- 定義reject函數(shù)
class MyPromise {
constructor(callback) {
// 初始化狀態(tài)為 pending
this.status = 'pending';
// 初始化成功狀態(tài)的值
this.value = undefined;
// 初始化失敗狀態(tài)的值
this.reason = undefined;
// 定義 resolve 函數(shù)
const resolve = value => {
if (this.status === 'pending') {
// 更新狀態(tài)為 resolved
this.status = 'resolved';
// 存儲成功狀態(tài)的值
this.value = value;
}
};
// 定義 reject 函數(shù)
const reject = reason => {
if (this.status === 'pending') {
// 更新狀態(tài)為 rejected
this.status = 'rejected';
// 存儲失敗狀態(tài)的值
this.reason = reason;
}
};
// 調(diào)用回調(diào)函數(shù),將 resolve 和 reject 傳遞給它
callback(resolve, reject);
}
}
二、then方法
接下來定義Promsie類中then函數(shù)。
- 首先創(chuàng)建一個Promise對象,根據(jù)Promise的狀態(tài)來執(zhí)行不同的回調(diào)函數(shù)。then函數(shù)接收兩個參數(shù),一個onResolved(Promise 的狀態(tài)為成功時候調(diào)用),一個onRejected(Promise 的狀態(tài)為失敗時候調(diào)用)。
- then函數(shù)返回一個新的Promsie對象,它的值取決于回調(diào)函數(shù)的返回值
- 如果當(dāng)前狀態(tài)是pending,需要將onResolved,onRejected回調(diào)保存起來,等異步結(jié)束之后再執(zhí)行
class MyPromise {
then(onResolved, onRejected) {
// 創(chuàng)建一個新的 Promise 對象
const newPromise = new MyPromise((resolve, reject) => {
// 如果當(dāng)前 Promise 的狀態(tài)為 resolved
if (this.status === 'resolved') {
try {
// 執(zhí)行 onResolved 回調(diào)函數(shù)
const x = onResolved(this.value);
// 處理返回值
resolve(x);
} catch (error) {
// 如果回調(diào)函數(shù)拋出異常,將異常作為失敗狀態(tài)的值
reject(error);
}
}
// 如果當(dāng)前 Promise 的狀態(tài)為 rejected
if (this.status === 'rejected') {
try {
// 執(zhí)行 onRejected 回調(diào)函數(shù)
const x = onRejected(this.reason);
// 處理返回值
resolve(x);
} catch (error) {
// 如果回調(diào)函數(shù)拋出異常,將異常作為失敗狀態(tài)的值
reject(error);
}
}
// 如果當(dāng)前 Promise 的狀態(tài)為 pending
if (this.status === 'pending') {
// 將 onResolved 和 onRejected 保存起來
// 等待異步操作完成后再執(zhí)行
this.onResolvedCallbacks.push(() => {
try {
const x = onResolved(this.value);
resolve(x);
} catch (error) {
reject(error);
});
this.onRejectedCallbacks.push(() => {
try {
const x = onRejected(this.reason);
resolve(x);
} catch (error) {
reject(error);
}
});
}
});
// 返回新的 Promise 對象
return newPromise;
}
三、catch方法
將 catch 方法轉(zhuǎn)化為 then 方法的一個語法糖,就可以實現(xiàn)啦。到這里我們基本已經(jīng)實現(xiàn)了一個Promise
class MyPromise {
catch(onRejected) {
return this.then(null, onRejected);
}
}
四、基礎(chǔ)完整版代碼
class MyPromise {
constructor(callback) {
// 初始化狀態(tài)為 pending
this.status = 'pending';
// 初始化成功狀態(tài)的值
this.value = undefined;
// 初始化失敗狀態(tài)的值
this.reason = undefined;
// 存儲成功狀態(tài)的回調(diào)函數(shù)
this.onResolvedCallbacks = [];
// 存儲失敗狀態(tài)的回調(diào)函數(shù)
this.onRejectedCallbacks = [];
// 定義 resolve 函數(shù)
const resolve = value => {
if (this.status === 'pending') {
// 更新狀態(tài)為 resolved
this.status = 'resolved';
// 存儲成功狀態(tài)的值
this.value = value;
// 執(zhí)行所有成功狀態(tài)的回調(diào)函數(shù)
this.onResolvedCallbacks.forEach(cb => cb());
}
};
// 定義 reject 函數(shù)
const reject = reason => {
if (this.status === 'pending') {
// 更新狀態(tài)為 rejected
this.status = 'rejected';
// 存儲失敗狀態(tài)的值
this.reason = reason;
// 執(zhí)行所有失敗狀態(tài)的回調(diào)函數(shù)
this.onRejectedCallbacks.forEach(cb => cb());
}
};
// 調(diào)用回調(diào)函數(shù),將 resolve 和 reject 傳遞給它
callback(resolve, reject);
}
// 創(chuàng)建一個新的 Promise 對象
const promise2 = new MyPromise((resolve, reject) => {
// 如果當(dāng)前 Promise 的狀態(tài)為 resolved
if (this.status === 'resolved') {
try {
// 執(zhí)行 onResolved 回調(diào)函數(shù)
const x = onResolved(this.value);
// 處理返回值
resolve(x);
} catch
(error) {
// 如果回調(diào)函數(shù)拋出異常,則將異常作為新 Promise 的失敗狀態(tài)的值
reject(error);
}
});
}
// 如果當(dāng)前 Promise 的狀態(tài)為 rejected
if (this.status === 'rejected') {
try {
// 執(zhí)行 onRejected 回調(diào)函數(shù)
const x = onRejected(this.reason);
// 處理返回值
resolve(x);
} catch (error) {
// 如果回調(diào)函數(shù)拋出異常,則將異常作為新 Promise 的失敗狀態(tài)的值
reject(error);
}
}
// 如果當(dāng)前 Promise 的狀態(tài)為 pending
if (this.status === 'pending') {
// 將 onResolved 和 onRejected 回調(diào)函數(shù)保存起來,等待異步操作完成后再執(zhí)行
this.onResolvedCallbacks.push(() => {
try {
const x = onResolved(this.value);
resolve(x);
} catch (error) {
reject(error);
}
});
this.onRejectedCallbacks.push(() => {
try {
const x = onRejected(this.reason);
resolve(x);
} catch (error) {
reject(error);
}
});
}
});
// 返回新的 Promise 對象
return promise2;
}
catch(onRejected) {
return this.then(null, onRejected);
}
}
五、案例測試
生成一個myPromsie對象,然后用then方法進行鏈?zhǔn)秸{(diào)用。
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
console.log('1')
resolve('成功')
}, 1000)
})
promise.then(value => {
console.log('2')
return "第一次"
}).then(value => {
console.log('3')
return new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('第二次處理結(jié)果');
}, 1000);
});
}).then(value => {
console.log(value);
throw new Error('拋出異常');
}).catch(error => {
console.log(error);
});
六、問題
1. 為什么then函數(shù)中需要考慮Promise狀態(tài)為pending的情況?
當(dāng) then 方法被調(diào)用時,我們首先需要判斷原始 Promise 對象的狀態(tài)。
- 如果原始 Promise 對象的狀態(tài)為 fulfilled,那么我們就可以直接執(zhí)行成功回調(diào)函數(shù),并將成功狀態(tài)的值作為參數(shù)傳遞給它。
- 如果原始 Promise 對象的狀態(tài)為 rejected,那么我們就可以直接執(zhí)行失敗回調(diào)函數(shù),并將失敗原因作為參數(shù)傳遞給它。
- 但是,如果原始 Promise 對象的狀態(tài)為 pending,那么我們就需要等待原始 Promise 對象的狀態(tài)發(fā)生變化,再執(zhí)行相應(yīng)的操作。
2. 當(dāng)then函數(shù)傳的參數(shù)不是函數(shù)怎么辦?
為了避免then函數(shù)傳的參數(shù)不是函數(shù),需要對上面代碼稍微優(yōu)化一下文章來源:http://www.zghlxwxcb.cn/news/detail-453455.html
then(onResolved, onRejected) {
onResolved = typeof onResolved === "function" ? onResolved : (value) => value;
onRejected = typeof onRejected === "function" ? onRejected : (reason) => { throw reason };
//其他邏輯
}
3. onResolvedCallbacks 和 onRejectedCallbacks 什么時候清空?
在調(diào)用then函數(shù)中,當(dāng)Promise的狀態(tài)為pending時候,會把onResolved和onRejected回調(diào)放到各自回調(diào)函數(shù)隊列中,等狀態(tài)改變(即在執(zhí)行resolve函數(shù)/reject函數(shù))時候,將 onResolvedCallbacks ,this.onRejectedCallbacks 循環(huán)調(diào)用。當(dāng)Promise狀態(tài)pending時候,就將 onResolvedCallbacks 和 onRejectedCallbacks 置空。所以優(yōu)化上面代碼如下:文章來源地址http://www.zghlxwxcb.cn/news/detail-453455.html
then(onResolved,onRejected){
if (this.status == "pending") {
this.onResolvedCallbacks.push(() => {
if (this.status == "resolved") {
try {
const x = onResolved(this.value)
resolve(x)
} catch (error) {
reject(error)
}
}
})
this.onRejectedCallbacks.push(() => {
if (this.status == "rejected") {
try {
const x = onRejected(this.reason)
resolve(x)
} catch (error) {
reject(error)
}
}
})
} else {
// 執(zhí)行完所有回調(diào)函數(shù)之后,清空回調(diào)數(shù)組
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
}
}
七、優(yōu)化后完整代碼
<script>
class MyPromise {
constructor(callback) {
this.status = "pending";
this.value = "";
this.reason = "";
// 存儲成功狀態(tài)的回調(diào)函數(shù)
this.onResolvedCallbacks = [];
// 存儲失敗狀態(tài)的回調(diào)函數(shù)
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.status == "pending") {
this.status = "resolved"
this.value = value;
this.onResolvedCallbacks.forEach((fn) => fn());
}
}
const reject = (reason) => {
if (this.status == "pending") {
this.status = "rejected"
this.reason = reason;
this.onRejectedCallbacks.forEach((fn) => fn());
}
}
try {
callback(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onResolved, onRejected) {
onResolved = typeof onResolved === "function" ? onResolved : (value) => value;
onRejected = typeof onRejected === "function" ? onRejected : (reason) => { throw reason };
const promise2 = new MyPromise((resolve, reject) => {
if (this.status == "resolved") {
console.log('1111111111')
try {
const x = onResolved(this.value)
resolve(x)
} catch (error) {
reject(error)
}
}
if (this.status == "rejected") {
console.log('2222222')
try {
const x = onRejected(this.reason)
resolve(x)
} catch (error) {
reject(error)
}
}
if (this.status == "pending") {
console.log('333333333333')
this.onResolvedCallbacks.push(() => {
if (this.status == "resolved") {
try {
const x = onResolved(this.value)
resolve(x)
} catch (error) {
reject(error)
}
}
})
this.onRejectedCallbacks.push(() => {
if (this.status == "rejected") {
try {
const x = onRejected(this.reason)
resolve(x)
} catch (error) {
reject(error)
}
}
})
} else {
// 執(zhí)行完所有回調(diào)函數(shù)之后,清空回調(diào)數(shù)組
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
}
})
return promise2
}
catch(onRejected) {
return this.then(null, onRejected)
}
}
const promise = new MyPromise((resolve, reject) => {
// setTimeout(() => {
// console.log('1')
resolve('成功')
// }, 1000)
})
promise.then(1).
then(value => {
// console.log('2')
// return "第一次"
// setTimeout(() => {
console.log('1')
// return "第一次"
// },1000)
}).then(value => {
console.log('3')
return new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('第二次處理結(jié)果');
}, 1000);
});
}).then(value => {
console.log(value);
throw new Error('拋出異常');
}).catch(error => {
console.log(error);
});
</script>
附
1. 處理promsie異常的三種方式
```javascript
function promise3() {
return new Promise(function(resolve, reject) {
var random = Math.random() * 10; // 隨機?個 1 - 10 的數(shù)字
setTimeout(function() {
if (random >= 5) {
resolve(random);
} else {
reject(random);
}
}, 1000);
});
}
var onResolve = function(val) {
console.log('已完成:輸出的數(shù)字是', val);
};
var onReject = function(val) {
console.log('已拒絕:輸出的數(shù)字是', val);
}
// promise 的 then 接收兩個函數(shù),第?個參數(shù)為 resolve 后執(zhí)?,第?個函數(shù)為 reject 后執(zhí)?
promise().then(onResolve, onReject);
// 也可以通過 .catch ?法攔截狀態(tài)變?yōu)橐丫芙^時的 promise
promise().catch(onReject).then(onResolve);
// 也可以通過 try catch 進?攔截狀態(tài)變?yōu)橐丫芙^的 promise
try {
promise().then(onResolve);
} catch (e) {
onReject(e);
}
到了這里,關(guān)于【JavaScript】手寫Promise的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!