async await 瞭解

問題引出:php

思考(案例來自stackoverflow):html

function foo(){ var result; $ajax({ url:'...', success:function(response){ result=response; //return response;//tried this one as well } }); return result; } var result=foo();

初學異步的時候,這裏是很容易錯的地方,你想要獲取從服務器端返回的數據,結果卻一直undefined。ajax

!!!氣死我了編程


 

在弄清楚此概念以前,先了解 JS 的異步機制:segmentfault

參考連接:http://www.javashuo.com/article/p-xackczmh-cr.html數組

http://www.javashuo.com/article/p-xqrpyxku-dd.htmlpromise

http://www.javashuo.com/article/p-ffewwzss-bq.html瀏覽器

http://www.ruanyifeng.com/blog/2015/05/async.html服務器

首先先了解什麼是同步和異步多線程

同步:

 在主線程上排隊執行的任務,只有前一個任務執行完畢,才能執行後一個任務;就像上面的圖中同樣,線性執行

異步:

若是函數是異步的,這個函數在被調用的時候,會立刻返回一個結果,可是這個結果可能不是預期的哦。(這是由於這個函數尚未獲得最終的結果,可是又不能讓人家一直等着,因此就先返回一個結果,致使不阻塞住)。等到這個函數終於知道結果了,若是有調用他的,他才告訴人家正確的結果。

下面以AJAX請求爲例,來看一下同步和異步的區別:

  • 同步ajax

主線程:「你好,AJAX線程。請你幫我發個HTTP請求吧,我把請求地址和參數都給你了。」

AJAX線程:「......」

主線程::「喂,AJAX線程,你怎麼不說話?」

AJAX線程:「......」

主線程::「喂!喂喂喂!」

AJAX線程:「......」

(一炷香的時間後)

主線程::「喂!求你說句話吧!」

AJAX線程:「主線程,很差意思,我在工做的時候不能說話。你的請求已經發完了,拿到響應數據了,給你。」

  • 異步ajax

主線程:「你好,AJAX線程。請你幫我發個HTTP請求吧,我把請求地址和參數都給你了。」

AJAX線程:「好的,主線程。我立刻去發,但可能要花點兒時間呢,你能夠先去忙別的。」

主線程::「謝謝,你拿到響應後告訴我一聲啊。」

(接着,主線程作其餘事情去了。一頓飯的時間後,它收到了響應到達的通知。)

 單線程語多線程、JS 單線程:

在上面介紹異步的過程當中就可能會納悶:既然JavaScript是單線程,怎麼還存在異步,那些耗時操做到底交給誰去執行了?

JavaScript其實就是一門語言,說是單線程仍是多線程得結合具體運行環境。JS的運行一般是在瀏覽器中進行的,具體由JS引擎去解析和運行。下面咱們來具體瞭解一下瀏覽器。

瀏覽器:

目前最爲流行的瀏覽器爲:Chrome,IE,Safari,FireFox,Opera。瀏覽器的內核是多線程的。

一個瀏覽器一般由如下幾個常駐的線程:

  • 渲染引擎線程:顧名思義,該線程負責頁面的渲染
  • JS引擎線程:負責JS的解析和執行
  • 定時觸發器線程:處理定時事件,好比setTimeout, setInterval
  • 事件觸發線程:處理DOM事件
  • 異步http請求線程:處理http請求

須要注意的是,渲染線程和JS引擎線程是不能同時進行的。渲染線程在執行任務的時候,JS引擎線程會被掛起。由於JS能夠操做DOM,若在渲染中JS處理了DOM,瀏覽器可能就不知所措了。

JS引擎:

一般講到瀏覽器的時候,咱們會說到兩個引擎:渲染引擎和JS引擎。渲染引擎就是如何渲染頁面,Chrome/Safari/Opera用的是Webkit引擎,IE用的是Trident引擎,FireFox用的是Gecko引擎。不一樣的引擎對同一個樣式的實現不一致,就致使了常常被人詬病的瀏覽器樣式兼容性問題。這裏咱們不作具體討論。

JS引擎能夠說是JS虛擬機,負責JS代碼的解析和執行。一般包括如下幾個步驟:

  • 詞法分析:將源代碼分解爲有意義的分詞
  • 語法分析:用語法分析器將分詞解析成語法樹
  • 代碼生成:生成機器能運行的代碼
  • 代碼執行

不一樣瀏覽器的JS引擎也各不相同,Chrome用的是V8,FireFox用的是SpiderMonkey,Safari用的是JavaScriptCore,IE用的是Chakra。

之因此說JavaScript是單線程,就是由於瀏覽器在運行時只開啓了一個JS引擎線程來解析和執行JS。那爲何只有一個引擎呢?若是同時有兩個線程去操做DOM,瀏覽器是否是又要不知所措了。

因此,雖然JavaScript是單線程的,但是瀏覽器內部不是單線程的。一些I/O操做、定時器的計時和事件監聽(click, keydown...)等都是由瀏覽器提供的其餘線程來完成的。

咱們常說「JavaScript是單線程的」。所謂單線程,是指在JS引擎中負責解釋和執行JavaScript代碼的線程只有一個。不妨叫它主線程。

可是實際上還存在其餘的線程。例如:處理AJAX請求的線程、處理DOM事件的線程、定時器線程、讀寫文件的線程(例如在Node.js中)等等。這些線程可能存在於JS引擎以內,也可能存在於JS引擎以外,在此咱們不作區分。不妨叫它們工做線程。

正是因爲JavaScript是單線程的,而異步容易實現非阻塞,因此在JavaScript中對於耗時的操做或者時間不肯定的操做,使用異步就成了必然的選擇。

 
 
解決辦法:
1. 經過回調函數解決
function foo(callback){//定義函數的時候將另外一個函數(回調函數)做爲參數傳入定義的函數中。 $ajax({ //... success:callback//異步操做執行完畢後,再執行該回調函數,確保回調在異步操做以後執行。 }); } function myCallback(result){ //... } foo(myCallback);
 
上面一段代碼解釋: 咱們定義了兩個函數 myCallback 和 foo。
  foo 函數裏面呢,是有異步操做的(ajax),按照正常狀況,他會返回一個不是真正結果的值,那咱們怎麼樣才能獲得真正的結果呢?就是當這個函數執行完的時候,調用 myCallback(這個函數是經過參數傳進來的),獲得這個真正的結果,就解決了這個問題。
優勢:比較容易理解;
缺點:1.高耦合,維護困難,回調地獄; 2.每一個任務只能指定一個回調函數; 3.若是幾個異步操做之間並無順序之分,一樣也要等待上一個操做執行結束再進行下一個操做。
 
2. Promise
 

先看上面這個函數是什麼意思: ajax 函數執行,而後是兩個 then 方法和一個 catch 方法 =》 ajax 裏面是啥呢? 是返回了一個 Promise 的東西 =》 Promise 裏面是啥呢? 是一系列函數執行,我也不知道這是執行的啥,反正就是在執行。

那就看看這個 Promise 是個什麼東西?煩死了,弄的稀奇古怪的。

Promise表明了一個異步操做,能夠將異步對象和回調函數脫離開來,經過.then方法在這個異步操做上綁定回調函數,Promise可讓咱們經過鏈式調用的方法去解決回調嵌套的問題。先捋一下, 應該就是 Promise 裏面的是一個異步操做,then 函數是在他執行完以後纔回去挨個執行的,應該是這個意思吧,和上面那種比就是換了個寫法嘛。

promise對象存在三種狀態:
1)Fulfilled:成功狀態
2)Rejected:失敗狀態
3)Pending:既不是成功也不是失敗狀態,能夠理解爲進行中狀態

promise對象的兩個重要方法:resolve/reject
1)resolve方法可使Promise對象的狀態改變爲成功,同時傳遞一個參數用於後續成功後的操做。
2)reject方法能夠將Promise對象的狀態改變爲失敗,同時將錯誤信息傳遞到後續錯誤處理的操做。

.then可使用鏈式調用,緣由在於:每一次執行該方法時總會返回一個Promise對象(Promise 對象的意義就是告訴成功仍是失敗吧)。
另外,在then的函數當中的返回值,能夠做爲後續操做的參數(例如:.then(return a).then(console.log(a+b))),這不就是線性執行了嘛!!!

 那麼問題來了,若是上面代碼異步操做拋出錯誤,會怎麼樣?會調用catch方法指定的回調函數,處理這個錯誤,並且then方法指定的回調函數,若是運行中拋出錯誤,也會被catch捕獲。Promise對象的錯誤具備「冒泡」性質,會一直向後傳遞,直到被捕獲爲止,也就是說,錯誤老是會被下一個catch語句捕獲。
 

理解Promise用法的關鍵點:
1.then方法是Promise實例的方法,即Promise.prototype上的,它的做用是爲Promise實例添加狀態改變時的回調函數,這個方法的第一個參數是resolved狀態的回調函數,第二個參數(可選)是rejected狀態的回調函數。
2.鏈式中的第二個then開始,它們的resolve中的參數,是前一個then中resolve的return語句的返回值。
3.關於執行順序:Promise在實例化的時候就會執行,也就是若是Promise的實例化語句中函數console.log輸出語句,它會比then中的先執行。Promise.all中傳入的Promise對象的數組(假設爲p一、p2),即便p2的運行速度比p1快,Promise.all方法仍然會按照數組中的順序將結果返回。
理解了上面這些方便寫原生的Promise,利用觀察者模式。後面補充。

Promise的缺點:
1.當處於未完成狀態時,沒法肯定目前處於哪一階段。
2.若是不設置回調函數,Promise內部的錯誤不會反映到外部。
3.沒法取消Promise,一旦新建它就會當即執行,沒法中途取消。

 

3.async/await:

不少人說async/await是異步編程的終極解決方案、
JavaScript 的 async/await 實現,離不開 Promise。

 

看上面這段代碼: 有一個 delay 函數,裏面有Promise關鍵字(代表是異步操做,你們讓讓,我先執行)。 在看 getAllBooks 函數, 它裏面有 await 這個關鍵字(意如其名,你們等等,等下面這個執行完了才能執行別的)

上面的 delay() 沒有申明爲 async。實際上,delay() 自己就是返回的 Promise 對象,加不加 async 結果都同樣。

只要在函數名以前加上async關鍵字,就代表這個函數內部有異步操做。這個異步操做返回一個Promise對象,前面用await關鍵字註明。函數執行的時候,一旦遇到await,就會先執行await後面的表達式中的內容(異步),再也不執行函數體後面的語句。等到異步操做執行完畢後,再自動返回到函數體內,繼續執行函數體後面的語句。

  async:定義異步函數

1)自動把函數轉換爲Promise
2)當調用異步函數時,函數返回值會被resolve處理
3)異步函數內部可使用await

await:暫停異步函數的執行
1)當使用在Promise前面時,await等待Promise完成,並返回Promise的結果
2)await只能和Promise一塊兒使用,不能和callback一塊兒使用
3)await只能用在async函數中

async/await並不會取代promise,由於async/await底層依然使用promise。

每次遇到 await 關鍵字時,Promise 都會停下在,await 把異步變成了同步。

相關文章
相關標籤/搜索