<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                [toc] ### [前期回顧]事件循環 #### 基本概念 - JS運行的環境稱之為宿主環境。 - 什么是執行棧:call stack,執行棧是一個數據結構,用于存放各種函數的執行環境,每一個函數執行之前,它的相關信息會加入到執行棧。函數調用之前,創建執行環境,然后加入到執行棧;函數調用之后,銷毀執行環境。整個JS運行時只會存在一個執行棧。 - JS在執行過程中會先在執行棧中建立一個全局上下文(相當于JS執行期預編譯的過程)。 - JS在執行代碼的過程中,每執行一個函數會在棧頂入棧一個該函數的執行上下文。 - JS引擎永遠執行的是執行棧的最頂部。 - 什么是異步函數:異步函數是指某些函數不會立即執行,需要等到某個時機到達后才會執行,這樣的函數稱之為異步函數。比如事件處理函數。異步函數的執行時機,會被宿主環境控制。 - 什么是線程:線程是操作系統能夠進行運算調度的最小單位,被包含在進程之中,是進程中的實際運作單位。一條線程指的是進程中一個單一順序的控制流。JS是單線程執行,執行代碼的過程中只有一個執行棧。 - 在瀏覽器的宿主環境中共包含5個線程: 1. JS引擎:負責執行執行棧的最頂部代碼 2. GUI線程:負責渲染頁面 3. 事件監聽線程:負責監聽各種事件 4. 計時線程:負責計時 5. 網絡線程:負責網絡通信 > 這5個線程中能給JS執行代碼的只有1個,且jS執行線程和GUI線程雖為獨立執行線程,但兩者間會相互等待。 #### 什么是事件循環 當上面的線程發生了某些事請,如果該線程發現,這件事情有處理程序,它會將該處理程序加入一個叫做事件隊列的內存。當JS引擎發現,執行棧中已經沒有了任何內容后,會將事件隊列中的第一個函數加入到執行棧中執行。 JS引擎對事件隊列的取出執行方式,以及與宿主環境的配合,稱之為事件循環(event loop)。 舉例說明(以瀏覽器為宿主環境): 1. JS線程在執行代碼時,遇到了需要滿足某些條件才會觸發的函數,例如onclick事件觸發A函數。 2. 此時并沒有發生點擊事件,JS會將A函數發送到事件監聽線程,然后繼續執行棧頂的上下文。 3. 事件監聽線程收到JS發來的A函數會按照約定的條件(如監聽哪個元素的點擊事件)進行監聽點擊事件。 4. 當點擊事件被觸發,事件監聽線程檢測到條件滿足,但其自身并不會執行A函數。會將A函數放入到事件隊列中(event queue)。 5. 當JS執行棧中已沒有上下文執行時,會從事件隊列中取出第一個函數加入到執行棧中執行(即JS執行棧執行A函數) > 這個從JS執行棧將需要滿中條件才能觸發的函數傳給事件監聽線程->事件監聽線程檢測到條件滿足將需要執行的函數發送到事件隊列->JS執行隊列中已無執行上下文后,再從事件隊列中取出第一個函數進行執行的過程。稱為事件循環。 *象這種需要滿足某些條件才能被執行的函數稱為異步函數,異步函數一定會被放到事件隊列中。這是與同步函數最根本的區別* 事件隊列在不同的宿主環境中有所差異,大部分宿主環境會將事件隊列進行細分。在瀏覽器中,事件隊列分為兩種: - 宏任務(隊列):macroTask,計時器結束的回調、事件回調、http回調等等絕大部分異步函數進入宏隊列 - 微任務(隊列):MutationObserver,Promise產生的回調進入微隊列 微任務隊列相對于宏任務隊列有執行優先權。 > MutationObserver用于監聽某個DOM對象的變化 當執行棧清空時,JS引擎首先會將微任務中的所有任務依次執行結束,如果沒有微任務,則執行宏任務。 ### 事件和回調函數的缺陷 我們習慣于使用傳統的回調或事件處理來解決異步問題 事件:某個對象的屬性是一個函數,當發生某一件事時,運行該函數 例如: ```js dom.onclick = function(){ //事件代碼 } ``` 回調:運行某個函數以實現某個功能的時候,傳入一個函數作為參數,當發生某件事的時候,會運行該函數。 例如: ```js dom.addEventListener("click", function(){ //函數代碼 }) ``` >本質上,事件和回調并沒有本質的區別,只是把函數放置的位置不同而已。 一直以來,該模式都運作良好。 直到前端工程越來越復雜... 目前,該模式主要面臨以下兩個問題: 1. 回調地獄:某個異步操作需要等待之前的異步操作完成,無論用回調還是事件,都會陷入不斷的嵌套 2. 異步之間的聯系:某個異步操作要等待多個異步操作的結果,對這種聯系的處理,會讓代碼的復雜度劇增 ### 異步處理的通用模型 ES官方參考了大量的異步場景,總結出了一套異步的通用模型,該模型可以覆蓋幾乎所有的異步場景,甚至是同步場景。 值得注意的是,為了兼容舊系統,ES6 并不打算拋棄掉過去的做法,只是基于該模型推出一個全新的 API,使用該API,會讓異步處理更加的簡潔優雅。 理解該 API,最重要的,是理解它的異步模型 1. ES6 將某一件可能發生異步操作的事情,分為兩個階段:**unsettled** 和 **settled** ![](https://img.kancloud.cn/ca/c3/cac3f24264184b18b043c1902d25c416_698x252.png) - unsettled: 未決階段,表示事情還在進行前期的處理,并沒有發生通向結果的那件事 - settled:已決階段,事情已經有了一個結果,不管這個結果是好是壞,整件事情無法逆轉 事情總是從 未決階段 逐步發展到 已決階段的。并且,未決階段擁有控制何時通向已決階段的能力。 2. ES6將事情劃分為三種狀態: pending、resolved、rejected - pending: 掛起,處于未決階段,則表示這件事情還在掛起(最終的結果還沒出來) - resolved:已處理,已決階段的一種狀態,表示整件事情已經出現結果,并是一個可以按照正常邏輯進行下去的結果 - rejected:已拒絕,已決階段的一種狀態,表示整件事情已經出現結果,并是一個無法按照正常邏輯進行下去的結果,通常用于表示有一個錯誤 既然未決階段有權力決定事情的走向,因此,未決階段可以決定事情最終的狀態! 我們將 把事情變為resolved狀態的過程叫做:**resolve**,推向該狀態時,可能會傳遞一些數據 我們將 把事情變為rejected狀態的過程叫做:**reject**,推向該狀態時,同樣可能會傳遞一些數據,通常為錯誤信息 **始終記住,無論是階段,還是狀態,是不可逆的!** ![](https://img.kancloud.cn/6d/a8/6da866a4fa60869e96c016d515747c51_859x468.png) 3. 當事情達到已決階段后,通常需要進行后續處理,不同的已決狀態,決定了不同的后續處理。 - resolved狀態:這是一個正常的已決狀態,后續處理表示為 thenable - rejected狀態:這是一個非正常的已決狀態,后續處理表示為 catchable 后續處理可能有多個,因此會形成作業隊列,這些后續處理會按照順序,當狀態到達后依次執行 ![](https://img.kancloud.cn/9c/d1/9cd163f9ce6c295edecb48457f3a6748_1163x609.png) 4. 整件事稱之為Promise ![](https://img.kancloud.cn/5a/09/5a095a599067f867c01ce7e05fca4bed_1191x626.png) **理解上面的概念,對學習Promise至關重要!** ### Promise的用法 #### Promise的基本概念 Promise是一個構造函數,Promise并沒有消除回調,其只是定義了一種特定的模式。讓我們按照該模式的執行順序編寫代碼。 #### 創建一個Promise ```js //創建一個Promise的標準寫法 const pro = new Promise((resolve, reject)=>{ // 此處編寫未決階段的一些處理代碼(例如:ajax的發送請求部份) // 通過調用resolve函數將Promise推向已決階段的resolved狀態 // 通過調用reject函數將Promise推向已決階段的rejected狀態 // resolve和reject均可以傳遞最多一個參數,表示推向狀態的數據,若需要傳多個參數,將其包裝成對象 }) //例如: const pro = new Promise((resolve, rejcet)=>{ console.log('我要去買一臺電腦'); //從此處開始就是未決階段的過程代碼/處理函數,這部份代碼會被立即執行 setTimeout(()=>{ if (Math.random() < 0.2){ resovle('買到了') //此處根據特定的條件,將事件推向已決階段。說明當滿足指定的條件后結果已經產生了 } else{ resovle('錢不夠') //此處根據特定的條件,將事件推向已決階段。說明當滿足指定的條件后結果已經產生了 } },2000) }) //上面的Promise中定義的代碼,是Promise處在未決階段所執行的代碼,會立即執行,用來定義根據條件變化將Promise推向哪個狀態。 //若為已決階段需要執行的代碼,使用下面的方式 pro.then(data=>{ //這是thenable函數,如果當前的Promise已經是resolved狀態,該函數會立即執行 //如果當前是未決階段,則會加入到作業隊列,等待到達resolved狀態后執行 //data為狀態數據,名稱可以自定義,其值為未決階段resolve返回的數據 }, err=>{ //這是catchable函數,如果當前的Promise已經是rejected狀態,該函數會立即執行 //如果當前是未決階段,則會加入到作業隊列,等待到達rejected狀態后執行 //err為狀態數據,名稱可以自定義,其值為在未決階段reject返回的數據或未決階段執行期間捕獲的錯誤信息 }) //then和err中的代碼均為異步函數,加入到事件隊列的微隊列中,會優先于宏隊列執行。 ``` >如何理解Promise中resovle和reject返回的結果: >當向目標發出一個請求,返回的結果是由目標給出的結果,無論結果如何(例如:返回有數據的結果/返回沒有數據的結果),至少目標做出了回應。這樣的結果應該由resolve返回。Promise從未決階段將狀態推向已決resovle階段,由Promise對象的then方法來處理結果。 >當向目標發出一個請求,返回的結果不是目標給出的,而是執行過程中出現的某些錯誤導致請求中斷,無法收到目標給出的結果。這樣的結果Promise會自動捕獲到并由reject返回,也可以將錯誤手動由reject返回。Promise從未決階段將狀態推向已決reject階段,由Promise對象的catch方法來處理結果。 #### Promise的相關細節 1. 未決階段的處理函數是同步的,會立即執行 2. thenable和catchable函數是異步的,就算是立即執行,也會加入到事件隊列中等待執行,并且,加入的隊列是微隊列 3. pro.then可以只添加thenable函數,pro.catch可以單獨添加catchable函數 4. 在未決階段的處理函數中,如果發生未捕獲的錯誤,會將狀態推向rejected,并會被catchable捕獲 5. 一旦狀態推向了已決階段,無法再對狀態做任何更改 6. **Promise并沒有消除回調,只是讓回調變得可控** ### Promise的串聯 當后續的Promise需要用到之前的Promise的處理結果時,需要Promise的串聯 Promise對象中,無論是then方法還是catch方法,它們都具有返回值,返回的是一個全新的Promise對象,它的狀態滿足下面的規則: 1. 如果當前的Promise是未決的,得到的新的Promise是掛起狀態 2. 如果當前的Promise是已決的,會運行響應的后續處理函數,并將后續處理函數的結果(返回值)作為resolved狀態數據,應用到新的Promise中;如果后續處理函數發生錯誤,則把返回值作為rejected狀態數據,應用到新的Promise中。 **后續的Promise一定會等到前面的Promise有了后續處理結果后,才會變成已決狀態** 如果前面的Promise的后續處理,返回的是一個Promise,則返回的新的Promise狀態和后續處理返回的Promise狀態保持一致。 ### Promise的其它相關API #### 原型成員 (實例成員) - then:注冊一個后續處理函數,當Promise為resolved狀態時運行該函數 - catch:注冊一個后續處理函數,當Promise為rejected狀態時運行該函數 - finally:[ES2018]注冊一個后續處理函數(無參),當Promise為已決時運行該函數 #### 構造函數成員 (靜態成員) - resolve(數據):該方法返回一個resolved狀態的Promise,傳遞的數據作為狀態數據 - 特殊情況:如果傳遞的數據是Promise,則直接返回傳遞的Promise對象 ```js const pro = new Promise((resolve, reject)=>{ resolve(1) }) //在某些情況下,如果定義的一個Promise沒有什么具體的執行過程,直接返回resolve,可以等同于下面的代碼 const pro = new Promise.resolve(1) ``` - reject(數據):該方法返回一個rejected狀態的Promise,傳遞的數據作為狀態數據。用法與上面的resolve相同。 - all(iterable):這個方法返回一個新的promise對象,該promise對象在iterable參數對象里所有的promise對象都成功的時候才會觸發成功,一旦有任何一個iterable里面的promise對象失敗則立即觸發該promise對象的失敗。這個新的promise對象在觸發成功狀態以后,會把一個包含iterable里所有promise返回值的數組作為成功回調的返回值,順序跟iterable的順序保持一致;如果這個新的promise對象觸發了失敗狀態,它會把iterable里第一個觸發失敗的promise對象的錯誤信息作為它的失敗錯誤信息。Promise.all方法常被用于處理多個promise對象的狀態集合。 >該方法必須全部為resolve時才會觸發成功 - race(iterable):當iterable參數里的任意一個子promise被成功或失敗后,父promise馬上會用子promise的成功返回值或失敗詳情作為參數調用父promise綁定的相應句柄,并返回該promise對象 >該方法會返回第一個有結果的Promise對象,無論該結果是resovle還是reject。 ### async和await簡介 async 和 await 是 ES2016 新增兩個關鍵字,它們借鑒了 ES2015 中生成器在實際開發中的應用,目的是簡化 Promise api 的使用,并非是替代 Promise。 #### async用法 目的是簡化在函數的返回值中對Promise的創建 async 用于修飾函數(無論是函數字面量還是函數表達式),放置在函數最開始的位置,被修飾函數的返回結果一定是 Promise 對象。 ```js async function test(){ console.log(1); return 2; } //等效于 function test(){ return new Promise((resolve, reject)=>{ console.log(1); resolve(2); }) } ``` #### await用法 **await關鍵字必須出現在async函數中!!!!** await用在某個表達式之前,如果表達式是一個Promise,則得到的是thenable中的狀態數據。 ```js async function test1(){ console.log(1); return 2; } async function test2(){ const result = await test1(); console.log(result); } test2(); ``` 等效于 ```js function test1(){ return new Promise((resolve, reject)=>{ console.log(1); resolve(2); }) } function test2(){ return new Promise((resolve, reject)=>{ test1().then(data => { const result = data; console.log(result); resolve(); }) }) } test2(); ``` 如果await的表達式不是Promise,則會將其使用Promise.resolve包裝后按照規則運行
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看