4、手寫promise
Promise
是一個內置對象,用于處理異步操作。Promise
對象表示一個尚未完成但預期將來會完成的操作。
Promise 的基本結構
一個 Promise
對象通常有以下狀態:
- pending(進行中):初始狀態,既不是成功也不是失敗。
- fulfilled(已成功):操作成功完成。
- rejected(已失敗):操作失敗。
promise原生代碼:
let promise = new Promise((resolve, reject) => {setTimeout(() => {resolve("下次一定");})});// then的用法promise.then((result) => {console.log(result);},(error) => {console.log(error);}).then((result) => {console.log(result);},(error) => {console.log(error);});
(1).初始結構
使用promise的基本結構:
let promise = new Promise((resolve, reject) => {resolve("下次一定");});
先創建一個Promise實例,傳入一個函數,函數有兩個參數,而且promise是有三種狀態的,執行reject還是resolve都要根據promise的狀態來定
// 因為promise的創建是:promise = new Promise(() => {})// 所以原生promose我們使用類class Commitment{// prmise有三種狀態,全局定義static PENDING = "待定";static FULFILLED = "成功";static REJECTED = "拒絕";// promise一般會傳入一個函數且參數為resolve和rejectconstructor(func){// 狀態this.status = Commitment.PENDING;// 結果this.result = null;this.error = null;// this調用自身的方法func(this.resolve , this.reject)}// resolveresolve(result){// 判斷狀態if(this.status === Commitment.PENDING){Commitment.status = Commitment.FULFILLED;this.result = result;}}// rejectreject(error){if(this.status === Commitment.PENDING){Commitment.status = Commitment.REJECTED;this.error = error;}}}
(2).this指向
但是我們測試發現了問題:
promise自己寫.html:38 Uncaught TypeError: Cannot read properties of undefined (reading 'status')at reject (promise自己寫.html:38:21)at promise自己寫.html:49:9at new Commitment (promise自己寫.html:24:13)at promise自己寫.html:47:21
Cannot read properties of undefined (reading ‘status’):this已經跟丟了
解決class的this指向問題:箭頭函數、bind或者proxy
constructor(func){// 狀態this.status = Commitment.PENDING;// 結果this.result = null;this.error = null;// this調用自身的方法,確保這些方法在被調用時,this 的值指向當前的 Commitment 實例func(this.resolve.bind(this) , this.reject.bind(this));}
bind(this)
會創建一個新的函數,這個新函數的this
值被永久綁定到當前Commitment
實例(即this
指向當前的Commitment
對象)。- 這樣,無論
resolve
或reject
方法在哪里被調用,this
始終指向Commitment
實例,確保你可以正確訪問實例的屬性和方法(如this.status
、this.result
等)。
(3).then
傳入兩個參數,一個是成功的回調,一個是失敗的回調,但是還要判斷條件
promise.then((result) => {console.log(result);},(error) => {console.log(error);})
then(onFULFILLED , onREJECTED){// 判斷狀態if(this.status === Commitment.FULFILLED){// 執行成功的回調onFULFILLED(this.result);}if(this.status === Commitment.REJECTED){// 執行失敗的回調onREJECTED(this.error);}}
(4).執行異常
1、執行報錯
// 測試const promise = new Commitment((resolve, reject) => {throw new Error("我報錯啦");});
拋出錯誤,要識別
// promise一般會傳入一個函數且參數為resolve和rejectconstructor(func){// 狀態this.status = Commitment.PENDING;// 結果this.result = null;this.error = null;// 在執行之前try...catch捕獲錯誤try{// this調用自身的方法,確保這些方法在被調用時,this 的值指向當前的 Commitment 實例func(this.resolve.bind(this) , this.reject.bind(this));}catch(error){// 捕獲錯誤this.reject(error);}}
2、當傳入的then參數不為函數的時候
// 測試const promise = new Commitment((resolve, reject) => {resolve("成功了");});promise.then (undefined,//成功error => { console.log("失敗:", error.message); } // 失敗回調(可選))
我們會發現它報錯了,解決方法就是判斷類型
promise自己寫.html:57 Uncaught TypeError: onFULFILLED is not a functionat Commitment.then (promise自己寫.html:57:17)at promise自己寫.html:72:13
// then方法:傳入兩個參數,一個是成功的回調,一個是失敗的回調// 但是還要判斷條件then(onFULFILLED , onREJECTED){// 判斷狀態以及是否為函數if(this.status === Commitment.FULFILLED && typeof onFULFILLED === 'function'){// 執行成功的回調onFULFILLED(this.result);}if(this.status === Commitment.REJECTED && typeof onREJECTED === 'function'){// 執行失敗的回調onREJECTED(this.error);}}
(5)異步
then實現異步–setTimeOut
// then方法:傳入兩個參數,一個是成功的回調,一個是失敗的回調// 但是還要判斷條件then(onFULFILLED , onREJECTED){// 判斷狀態以及是否為函數if(this.status === Commitment.FULFILLED && typeof onFULFILLED === 'function'){// 執行成功的回調onFULFILLED(this.result);}if(this.status === Commitment.REJECTED && typeof onREJECTED === 'function'){// 執行失敗的回調onREJECTED(this.error);}}
resolve是異步的,then也是調用的
原生的promise:
console.log("第一步");let promise = new Promise((resolve, reject) => {console.log("第二步");setTimeout(() => {resolve("下次一定");console.log("第四步");})});// then的用法promise.then((result) => {console.log(result);},(error) => {console.log(error);})console.log("第三步");
第一步
第二步
第三步
第四步
下次一定
但是手寫promise
console.log("第一步");const promise = new Commitment((resolve, reject) => {console.log("第二步");setTimeout(() => { //執行thenresolve("成功了");console.log("第四步");}, 0);});promise.then (result => {console.log("成功" , result.message); },//成功error => { console.log("失敗:", error.message); } // 失敗回調(可選))console.log("第三步");
第一步
第二步
第三步
第四步
原因:then中狀態判斷的問題,settimeout之后它就執行then方法了,但是此時的狀態還是待定,但是then里面并沒有處理待定狀態
(6)回調保存
解決:判定待定狀態,保留then里面的函數,所以我們要用數組保存函數
但是我們發現還是有問題resolve和reject是在事件循環末尾執行的,他們也要添加resolve和reject
<script>// 因為promise的創建是:promise = new Promise(() => {})// 所以原生promose我們使用類class Commitment {// prmise有三種狀態,全局定義static PENDING = "待定";static FULFILLED = "成功";static REJECTED = "拒絕";// promise一般會傳入一個函數且參數為resolve和rejectconstructor(func) {// 狀態this.status = Commitment.PENDING;// 結果this.result = null;this.error = null;// 數組:用于保存成功和失敗的回調this.onFulfilledCallbacks = [];//新增this.onRejectedCallbacks = [];//新增// 在執行之前try...catch捕獲錯誤try {// this調用自身的方法,確保這些方法在被調用時,this 的值指向當前的 Commitment 實例func(this.resolve.bind(this), this.reject.bind(this));} catch (error) {// 捕獲錯誤this.reject(error);}}// resolveresolve(result) {setTimeout(() => {// 判斷狀態if (this.status === Commitment.PENDING) {this.status = Commitment.FULFILLED;this.result = result;// 執行成功的回調--新增this.onFulfilledCallbacks.forEach((callback) => {callback(result);});}});}// rejectreject(error) {setTimeout(() => {if (this.status === Commitment.PENDING) {this.status = Commitment.REJECTED;this.error = error;// 執行失敗的回調--新增this.onRejectedCallbacks.forEach((callback) => {callback(error);});}});}// then方法:傳入兩個參數,一個是成功的回調,一個是失敗的回調// 但是還要判斷條件then(onFULFILLED, onREJECTED) {// 判斷狀態以及是否為函數// 如果是待定,就要保存函數到數組里面--新增if (this.status === Commitment.PENDING) {this.onFulfilledCallbacks.push(onFULFILLED);this.onRejectedCallbacks.push(onREJECTED);}if (this.status === Commitment.FULFILLED &&typeof onFULFILLED === "function") {// then是異步的setTimeout(() => {// 執行成功的回調onFULFILLED(this.result);});}if (this.status === Commitment.REJECTED &&typeof onREJECTED === "function") {// then是異步的setTimeout(() => {// 執行失敗的回調onREJECTED(this.error);});}}}// 測試console.log("第一步");const promise = new Commitment((resolve, reject) => {console.log("第二步");setTimeout(() => {resolve("成功了");console.log("第四步");}, 0);});promise.then((result) => {console.log(result);}, //成功(error) => {console.log(error);} // 失敗回調(可選));console.log("第三步");</script>
(7)鏈式
promise的鏈式是新建一個promise對象,我們直接返回this就好了
then(onFULFILLED, onREJECTED) {// 判斷狀態以及是否為函數// 如果是待定,就要保存函數到數組里面if (this.status === Commitment.PENDING) {this.onFulfilledCallbacks.push(onFULFILLED);this.onRejectedCallbacks.push(onREJECTED);}if (this.status === Commitment.FULFILLED &&typeof onFULFILLED === "function") {// then是異步的setTimeout(() => {// 執行成功的回調onFULFILLED(this.result);});}if (this.status === Commitment.REJECTED &&typeof onREJECTED === "function") {// then是異步的setTimeout(() => {// 執行失敗的回調onREJECTED(this.error);});}return this; // 返回當前的promise對象}}
為什么直接返回this可以實現回調呢???
方法調用和返回值
在 JavaScript 中,方法的調用(例如 obj.method()
)會返回方法的返回值。
如果方法返回 this
(即對象本身),那么調用該方法后,你仍然持有對該對象的引用。
鏈式調用的實現
當你調用
obj.method1().method2()
時:
obj.method1()被調用,并返回
obj(因為
method1返回
this)。
然后
method2()被調用,其調用者仍然是
obj`。
這樣,你可以連續調用多個方法,形成鏈式調用。
**總結:**手寫 Promise 的核心在于實現狀態管理、異步回調和鏈式調用。首先,Promise 有三種狀態(pending/fulfilled/rejected),通過構造函數接收執行器函數,并用 resolve
和 reject
更新狀態。
關鍵點包括:**狀態管理:**初始為 pending
,調用 resolve
或 reject
后不可逆地變為 fulfilled
或 rejected
。異步回調:then
方法需異步執行回調(用 setTimeout
),若狀態為 pending
,需將回調存入數組,待狀態變更后遍歷執行。**錯誤捕獲:**構造函數中用 try/catch
捕獲執行器函數的同步錯誤,并調用 reject
。鏈式調用:then
返回 this
,使后續 then
能繼續綁定回調,形成鏈式調用。
最終實現需確保:
- 狀態變更后異步觸發回調。
- 回調參數類型檢查(非函數則忽略)。
象的引用。