天天看點

JavaScript 處理異步的方法 JS

javascript語言的執行環境是單線程(single thread),就是指一次隻能完成一件任務。如果有多個任務,就必須排隊,前面一個任務完成,再執行後面一個任務,以此類推。

這種模式的好處是實作起來比較簡單,執行環境相對單純;但是隻要耗時比較多,假如有一個任務耗時很長,後面的任務都必須排隊等着,會拖延整個程式的執行。為了解決這個問題,Javascript語言将任務的執行模式分成兩種:同步(Synchronous)和異步(Asynchronous)。

同步模式:就是一個任務先執行,後一個任務等待前一個任務結束,然後再執行,程式的執行順序與任務的排列順序是一緻的、同步的;

異步模式::每一個任務有一個或多個回調函數(callback),前一個任務結束後,不是執行後一個任務,而是執行回調函數,後一個任務則是不等前一個任務結束就執行,是以程式的執行順序與任務的排列順序是不一緻的、異步的。

Javascript處理異步的方法有以下幾種:

回調是一個函數被作為一個參數傳遞到另一個函數裡,在那個函數執行完後再執行。回調函數是異步程式設計最基本的方法,其優點是簡單、容易了解和部署;缺點是容易産生回調地獄。

這就是所謂的回調地獄,回調地獄帶來的負面作用有以下幾點:

代碼臃腫,可讀性差,可維護性差。

代碼複用性差。

容易滋生 bug。

隻能在回調裡處理異常。

這種方式,異步任務的執行不取決于代碼的順序,而取決于某個事件是否發生。

上面這行代碼的意思是,當f1發生done事件,就執行f2。

優點:寫法相容到主流浏覽器;

缺點:當同一個element元素綁定多個事件時,隻有最後一個事件會被添加

該方法的第三個參數是一個布爾值:當為false時表示由裡向外,true表示由外向裡。

我們假定,存在一個"信号中心",某個任務執行完成,就向信号中心"釋出"(publish)一個信号,其他任務可以向信号中心"訂閱"(subscribe)這個信号,進而知道什麼時候自己可以開始執行。這就叫做"釋出/訂閱模式"(publish-subscribe pattern)

首先,f2向信号中心jQuery訂閱done信号。

然後,f1進行如下改寫:

f1執行完成後,向信号中心jQuery釋出<code>done</code>信号,進而引發f2的執行。f2完成執行後,可以取消訂閱(unsubscribe)

這種方式的優點:可以通過檢視“消息中心”,了解存在多少信号、每個信号有多少訂閱者,進而監控程式的運作。

以上都是ES6之前的異步處理方式。ES6之後出現了<code>promise</code>。它是異步程式設計的一種解決方案,比傳統的解決方案(回調函數)——更合理和更強大。

Promise 對象有以下兩個特點。

對象的狀态不受外界影響。Promise 對象代表一個異步操作,有三種狀态:pending(進行中)、fulfilled(已成功)和rejected(已失敗)。隻有異步操作的結果,可以決定目前是哪一種狀态,任何其他操作都無法改變這個狀态。

一旦狀态改變,就不會再變,任何時候都可以得到這個結果

(1) ES6 規定,Promise 對象是一個構造函數,用來生成 Promise 執行個體。

Promise接收一個函數作為參數,函數裡有resolve和reject兩個參數:

resolve方法的作用是将Promise的pending狀态變為fullfilled,在異步操作成功之後調用,可以将異步傳回的結果作為參數傳遞出去。

reject方法的作用是将Promise的pending狀态變為rejected,在異步操作失敗之後調用,可以将異步傳回的結果作為參數傳遞出去。

他們之間隻能有一個被執行,不會同時被執行,因為Promise隻能保持一種狀态。

第一個參數執行的是resolve()方法(即異步成功後的回調方法)

第二參數執行的是reject()方法(即異步失敗後的回調方法)(第二個參數可選)。

它傳回的是一個新的Promise對象。

2.Promise.finally()

Promise.finally()用于指定不管 Promise 對象最後狀态如何,都會執行的操作。

3.Promise.all()

Promise.all()用于處理多個異步處理,比如說一個頁面上需要等多個 ajax 的資料回來才執行相關邏輯。

隻有p1、p2、p3的狀态都變成fulfilled,p的狀态才會變成fulfilled,此時p1、p2、p3的傳回值組成一個數組,傳遞給p的回調函數。

隻要p1、p2、p3之中有一個被rejected,p的狀态就變成rejected,此時第一個被reject的執行個體的傳回值,會傳遞給p的回調函數。

4.Promse.race()

Promse.race()就是賽跑的意思,Promise.race([p1, p2, p3])裡面哪個結果獲得的快,就傳回那個結果,不管結果本身是成功狀态還是失敗狀态。

上面代碼中,隻要p1、p2、p3之中有一個執行個體率先改變狀态,p的狀态就跟着改變。那個率先改變的 Promise 執行個體的傳回值,就傳遞給p的回調函數。

<code>async/await</code>是<code>JavaScript</code>為了解決異步問題而提出的一種解決方案,許多人将其稱為異步的終極解決方案。<code>async</code> 函數,就是 <code>Generator</code> 函數的文法糖。

相較于 <code>Generator</code>,<code>Async</code> 函數的改進在于下面四點:

内置執行器。Generator 函數的執行必須依靠執行器,而 Aysnc 函數自帶執行器,調用方式跟普通函數的調用一樣。

更好的語義。async 和 await 相較于 * 和 yield 更加語義化。

更廣的适用性。co 子產品約定,yield 指令後面隻能是 Thunk 函數或 Promise對象。而 async 函數的 await 指令後面可以是 Promise 或者原始類型(Number,string,boolean,但這時等同于同步)。

傳回值是 Promise。async 函數傳回值是 Promise 對象,比 Generator 函數傳回的 Iterator 對象友善,可以直接使用 then() 方法進行調用。

(1) 凡是在前面添加了async的函數在執行後都會自動傳回一個Promise對象

(2) await必須在async函數裡使用,不能單獨使用

如果await等到的不是一個promise對象,那跟着的表達式的運算結果就是它等到的東西;

如果是一個promise對象,await會阻塞後面的代碼,等promise對象resolve,得到resolve的值作為await表達式的運算結果

雖然await阻塞了,但await在async中,async不會阻塞,它内部所有的阻塞都被封裝在一個promise對象中異步執行

繼續閱讀