JS手寫代碼篇---手寫Promise

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 對象)。
  • 這樣,無論 resolvereject 方法在哪里被調用,this 始終指向 Commitment 實例,確保你可以正確訪問實例的屬性和方法(如 this.statusthis.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),通過構造函數接收執行器函數,并用 resolvereject 更新狀態。

關鍵點包括:**狀態管理:**初始為 pending,調用 resolvereject 后不可逆地變為 fulfilledrejected異步回調:then 方法需異步執行回調(用 setTimeout),若狀態為 pending,需將回調存入數組,待狀態變更后遍歷執行。**錯誤捕獲:**構造函數中用 try/catch 捕獲執行器函數的同步錯誤,并調用 reject鏈式調用:then 返回 this,使后續 then 能繼續綁定回調,形成鏈式調用。

最終實現需確保:

  • 狀態變更后異步觸發回調。
  • 回調參數類型檢查(非函數則忽略)。
    象的引用。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/83971.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/83971.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/83971.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

我喜歡的vscode幾個插件和主題

主題 Monokaione Monokai Python 語義高光支持 自定義顏色為 self 將 class , def 顏色更改為紅色 為裝飾器修復奇怪的顏色 適用于魔法功能的椂光 Python One Dark 這個主題只在python中效果最好。 我為我個人使用做了這個主題,但任何人都可以使用它。 插件 1.Pylance Pylanc…

【深度學習新浪潮】大模型時代,我們還需要學習傳統機器學習么?

在大模型時代,AI 工程師仍需掌握傳統機器學習知識,這不僅是技術互補的需求,更是應對復雜場景和職業發展的關鍵。以下從必要性和學習路徑兩方面展開分析: 一、傳統機器學習在大模型時代的必要性 技術互補性 大模型(如GPT、BERT)擅長處理復雜語義和生成任務,但在數據量少…

年度工作計劃總結述職報告PPT模版一組分享

工作計劃總結述職報告PPT模版&#xff1a;工作計劃述職報告PPT模版https://pan.quark.cn/s/fba40a5e87da 第一套PPT模版是醫院年度工作計劃的封面頁&#xff0c;有藍橙配色、醫院標題、年度工作計劃的大字、英文副標題、匯報人信息和右上角的醫院logo區域&#xff0c;右側還有醫…

軟件設計師“排序算法”真題考點分析——求三連

一、考點分值占比與趨勢分析 綜合知識題分值統計表 年份考題數量總分值分值占比考察重點2018222.67%時間復雜度/穩定性判斷2019334.00%算法特性對比分析2020222.67%空間復雜度要求2021111.33%算法穩定性判斷2022334.00%綜合特性應用2023222.67%時間復雜度計算2024222.67%分治…

華為云Flexus+DeepSeek征文|基于華為云Flexus云服務的云服務器單機部署Dify-LLM應用開發平臺

目錄 一、前言 二、華為云Flexus云服務優勢 三、華為云Flexus一鍵部署Dify 3.1 選擇模板 3.2 參數配置 3.3 資源棧設置 3.4 配置確認 3.5 創建執行計劃 3.6 部署 四、Dify-LLM應用開發平臺初體驗 4.1 訪問Dify-LLM應用開發平臺 4.2 設置管理員賬戶 4.3 登錄Dify-LLM應用開發平臺…

智能指針RAII

引入&#xff1a;智能指針的意義是什么&#xff1f; RAll是一種利用對象生命周期來控制程序資源&#xff08;如內存、文件句柄、網絡連接、互斥量等等&#xff09;的簡單技術。 在對象構造時獲取資源&#xff0c;接著控制對資源的訪問使之在對象的生命周期內始終保持有效&#…

nt!MiRemovePageByColor函數分析之脫鏈和刷新顏色表

第0部分&#xff1a;背景 PFN_NUMBER FASTCALL MiRemoveZeroPage ( IN ULONG Color ) { ASSERT (Color < MmSecondaryColors); Page FreePagesByColor[Color].Flink; if (Page ! MM_EMPTY_LIST) { // // Remove the first entry on the zeroe…

DEBUG:Lombok 失效

DEBUG&#xff1a;Lombok 失效 問題描述 基于 Spring Boot 的項目中&#xff0c;編譯時顯示找不到 log 屬性。查看對應的 class 類&#xff0c;Lombok 正常在編譯時生成 log 屬性。 同時存在另一個問題&#xff0c;使用Getter注解&#xff0c;但實際使用中該注解并沒有生效&…

3D幾何建模引擎3D ACIS Modeler核心功能深度解讀

3D ACIS Modeler是一款由Spatial Corporation&#xff08;現為Dassault Systmes旗下&#xff09;開發的工業級三維幾何建模內核&#xff0c;為CAD/CAM/CAE、建筑、制造、測量及三維動畫等領域提供底層建模能力。本文將從基本定位、核心功能及行業案例三方面&#xff0c;系統介紹…

Flutter - 集成三方庫:數據庫(sqflite)

數據庫 $ flutter pub add sqlite $ flutter pub get$ flutter run運行失敗&#xff0c;看是編譯報錯,打開Xcode工程 ? B 編譯 對比 GSYGithubAppFlutter 的Xcode工程Build Phases > [CP] Embed Pods Frameworks 有sqfite.framework。本地默認的Flutter工程默認未生成Pod…

Android 中 權限分類及申請方式

在 Android 中,權限被分為幾個不同的類別,每個類別有不同的申請和管理方式。 一、 普通權限(Normal Permissions) 普通權限通常不會對用戶隱私或設備安全造成太大風險。這些權限在應用安裝時自動授予,無需用戶在運行時手動授權。 android.permission.INTERNETandroid.pe…

目標檢測指標計算

mAP&#xff08;mean Average Precision&#xff09; 概述 預備參數&#xff1a;類別數&#xff0c;IoU閾值&#xff0c;maxDets值&#xff08;每張測試圖像最多保留maxDets個預測框&#xff0c;通常是根據置信度得分排序后取前maxDets個&#xff09;&#xff1b; Q: 假如某張…

聯合索引失效情況分析

一.模擬表結構&#xff1a; 背景&#xff1a; MySQL版本——8.0.37 表結構DDL&#xff1a; CREATE TABLE unite_index_table (id bigint NOT NULL AUTO_INCREMENT COMMENT 主鍵,clomn_first varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMEN…

軟件架構之-論分布式架構設計及其實現

論分布式架構設計及其實現 摘要正文摘要 2023年2月,本人所在集團公司承接了長三角地區某省漁船圖紙電子化審查項目開發,該項目旨在為長三角地區漁船建造設計院、漁船審圖機構提供一個便捷化的服務平臺。在次項目中,我作為項目成員參與了整個項目的建設工作,全權負責項目需求…

Pydantic數據驗證實戰指南:讓Python應用更健壯與智能

導讀&#xff1a;在日益復雜的數據驅動開發環境中&#xff0c;如何高效、安全地處理和驗證數據成為每位Python開發者面臨的關鍵挑戰。本文全面解析了Pydantic這一革命性數據驗證庫&#xff0c;展示了它如何通過聲明式API和類型提示系統&#xff0c;徹底改變Python數據處理模式。…

3、ubantu系統 | 通過vscode遠程安裝并配置anaconda

1、vscode登錄 登錄后通過pwd可以發現目前位于wangqinag賬號下&#xff0c;左側為屬于該賬號的文件夾及文件。 通過cd ..可以回到上一級目錄&#xff0c;通過ls可以查看當前目錄下的文件夾及文件。 2、安裝 2.1、下載anaconda 通過wget和curl下載未成功&#xff0c;使用手動…

Python 與 Java 在 Web 開發中的深度對比:從語言特性到生態選型

在 Web 開發領域&#xff0c;Python 和 Java 作為兩大主流技術棧&#xff0c;始終是開發者技術選型時的核心考量。本文將從語言本質、框架生態、性能工程、工程實踐等多個維度展開深度對比&#xff0c;結合具體技術場景解析兩者的適用邊界與融合方案&#xff0c;為開發者提供系…

【OpenGL學習】(一)創建窗口

文章目錄 【OpenGL學習】&#xff08;一&#xff09;創建窗口 【OpenGL學習】&#xff08;一&#xff09;創建窗口 GLFW OpenGL 本身只是一套圖形渲染 API&#xff0c;不提供窗口創建、上下文管理或輸入處理的功能。 GLFW 是一個支持創建窗口、處理鍵盤鼠標輸入和管理 OpenGL…

電腦閃屏可能的原因

1. 顯示器 / 屏幕故障 屏幕排線接觸不良&#xff1a;筆記本電腦屏幕排線&#xff08;屏線&#xff09;松動或磨損&#xff0c;導致信號傳輸不穩定&#xff0c;常見于頻繁開合屏幕的設備。屏幕面板損壞&#xff1a;液晶屏內部燈管老化、背光模塊故障或面板本身損壞&#xff0c;…

docker容器知識

一、docker與docker compose區別&#xff1a; 1、docker是創建和管理單個容器的工具&#xff0c;適合簡單的應用或服務&#xff1b; 2、docker compose是管理多容器應用的工具&#xff0c;適合復雜的、多服務的應用程序&#xff1b; 3、docker與docker compose對比&#xff…