說說異步

異步出現的前提

首先,由於JavaScript語言是單線程的*(目標爲瀏覽器端,出生即爲單線程)*,因此就須要異步,不然JavaScript腳本智能自上而下執行,若是在上部存在一些極其複雜的代碼須要解析很長的時間的話,下面的代碼就會遭到阻塞,也就是用戶感覺到的卡死.javascript

異步如何實現

因爲是單線程語言,因此JavaScript實現異步的方法是經過**事件循環(event loop)**來實現異步.html

console.log('I’m first one code!');

setTimeout(() => {
    console.log('I’m setTimeout function code!');
})

console.log('I’m last one code! ');
複製代碼

這段代碼的運行結果爲java

I’m first one code!
I’m last one code!
I’m setTimeout function code!
複製代碼

因此,代碼並無是自上而下執行,setTimeout函數是延遲了一段時間,等其餘語句執行完了採起執行,這種狀況就爲異步。es6

eventloop的機制

根據上一部分咱們知道,JavaScript把事件分爲兩類:同步與異步api

因此JavaScript的執行機制實際上是:promise

  • 判斷一個任務是同步任務仍是異步任務,同步進入主線程,異步進入event table
  • 異步任務在event table中註冊函數,知足該函數觸發條件後,推入事件隊列
  • 同步任務在主線程按順序執行,當主線程空閒時,再去事件隊列中查看是否有可執行的異步任務,若是有就進入主線程

這個循環即爲 event loop瀏覽器

異步也有小區別

咱們先看一段代碼bash

setTimeout(() => {
    console.log('1');
});

new Promise((resolve) => {
    console.log('2');
}).then( () => {
    console.log('3');
});

console.log('4');
複製代碼

若是按照咱們剛纔的理解,這段代碼的結果應該爲 2,4,1,3異步

But unfortunately!他的結果爲 2,4,3,1async

這也引入了另外兩個概念:宏任務微任務

因此任務應該分爲這兩類:

  • 宏任務(macro-task): 包括整段script代碼,setTimeout,setInterval
  • 微任務(micro-task): Promise,catch, finally,process.nextTick(Node端)

因此js的執行機制實際上是

先執行一個宏任務,過程當中若是遇到微任務先把他放到微任務的事件隊列中,當宏任務執行完畢後,再去查看微任務的事件隊列,將微任務一次執行完,執行完畢後再去進行下一個隊列的宏任務,以此循環.

借用一張圖演示

介紹主角Async

剛忽然發現已經繞遠了:car:,趕快漂移回原來的主題,async,一個在ES2017中提出的異步方案,有人說他是Generator函數的語法糖,只是把Generator函數的 * 替換爲 async,把yield替換爲await。咱們先不討論這句話說得對不對:speak_no_evil:,但它確實是基於了Generator的一種改進,它讓異步變得更簡單了。

他作了什麼?

若是要簡述的話,一旦你的函數前帶上了async,你的函數返回值就一定是promise對象.(他就像真香定律同樣是沒有能夠逃過的)就算你寫的函數裏返回的不是promise,他也會自動用Promise.resolve()包裝起來讓他成爲一個promise對象。

因此,若是咱們簡單理解async關鍵字的話,他其實就是給函數加上一個標識,說明這個函數內部有異步操做。

What is await waiting for?

咱們再次簡單的介紹如下 await,await 其實等的是右側表達式的結果.

若是右側是一個函數,則是這個函數的返回值。若是是一個值則就爲此值.

咱們經過一個例子來"見識見識"它

async function fun1(){
    console.log('fun1 is started!');
    await fun2();
    console.log('fun1 ending!');
}

async function fun2(){
    console.log('fun2 is running!');
}
fun1();
console.log('script start');
複製代碼

咱們知道,await是經過執行到此時讓出線程,經過阻塞後面的代碼來執行,但咱們執行上面的代碼發現結果爲

fun1 is started!
fun2 is running!
script start
fun1 endding!
複製代碼

注意,這裏fun2先於"script start" 執行,因此 await 的那個表達式的執行順序實際上是從右到左,即爲執行了fun2後讀到了await關鍵字,而後阻塞後面的代碼,這點很是重要,由於以前由於"一旦遇到await就立馬退出線程,阻塞後面的代碼"的觀點,認爲 await也會阻塞他後面的那個表達式,但其實否則。

await 與 async 的關係就像魚和水, await必需要有async才能夠存在,而async卻不必定須要有await。

await 的下一步操做

通常來講 await等到的右側表達式結果有兩種狀況:

Promise or Not Promise。

  • 若是不是promise,await會阻塞以後的代碼,就先去執行async外面的同步代碼,同步代碼執行完後再回到async內部,把這個不是promise的結果做爲await的結果。
  • 若是是promise對象,await也會先阻塞async後面的代碼,而後執行async外面的同步代碼,等待這個promise對象到達fulfilled狀態後,把 resolve 的參數做爲 await的運算結果。其實就是執行了await Promise.resolve(),這裏不作詳細解釋.

Async 與 Promise 和 Generator的一些比較

  • 首先Promise的提出是解決了以前使人頭疼的回調地獄(callback hell)問題,但直觀的看上去其實就像用了一個類庫,經過Promise的api來完成了異步操做,操做自己的可調控性不是很高,但已經很實用了。
  • Generator函數的語義相較於Promise清晰了許多,但問題就是若是他要自動執行的話必須實用任務運行器來自動執行它。
  • Async相較於Generator函數實現起來相對簡潔,更貼切語義。它在語言層面提供了Generator的自動執行器,代碼量也大大減小。

參考連接

ECMAScript 6 標準入門

微任務、宏任務與Event-loop

javascript的宏任務與微任務

我的Github:Reaper622

歡迎學習交流

相關文章
相關標籤/搜索