JavaScript異步史

什麼是異步編程

什麼是異步編程,異步編程簡單來講就是:執行一個指令不會立刻返回結果而執行下一個任務,而是等到特定的事件觸發後,才能獲得結果。javascript

異步編程時就須要指定異步任務完成後須要執行的指令,總的來講有如下幾種「指定異步指令」的方式:java

  1. 屬性
  2. 回調
  3. Promise
  4. Generator
  5. await,async

屬性

早期的javascript的異步的實現也相似於這種類的屬性的方式:每一個類實例的相關回調事件有相應的handler(onclick,onchange,onload) 等。node

在DOM0級事件處理程序,就是將一個函數賦值給一個元素的屬性。git

element.onclick=function(){
    console.log("clicked");
}
window.onload=function(){
    console.log("loaded");
}
複製代碼

回調(發佈/訂閱)

因爲javascript支持函數式編程,JavaSCript語言對異步編程的實現能夠用回調函數。 DOM2級事件解決了這個問題以上兩個問題github

element.addEventListener("click",function(){
    console.log("clicked");
});
複製代碼

Promise

Promise很好的解決了"並行"的問題,咱們看看用promise庫怎麼發送get請求:編程

import fetch from 'node-fetch';
fetch('https://api.github.com')
.then((res)=>res.json())
.then((json)=>console.log("json:",json))
複製代碼

能夠看到promise把原來嵌套的回調,改成級連的方式了,實際是一種代理(proxy)。json

新建一個promise實例:api

let promise = new Promise((resolve, reject)=>{
    // 異步操做的代碼
    if(/* 異步操做成功 */){
        resolve(value);
    } else {
        reject(error);
    }
});
複製代碼

promise把成功和失敗分別代理到resolved 和 rejected . 同時還能夠級連catch異常。數組

Generator

簡單來講generator能夠理解爲一個可遍歷的狀態機。promise

語法上generator,有兩個特徵:

  1. function關鍵字與函數名以前有一個星號。
  2. 函數體內部使用 yield關鍵字,定義不一樣的內部狀態。

因爲generator是一個狀態機,因此須要手動調用 next才能執行,但TJ大神開發了co模塊,能夠自動執行generator。

import co from 'co';
co(function* (){
    let now = Date.now();
    yield sleep(150); // 約等待150ms
    console.log(Date.now() - now);
});

function sleep(ms){
    return function(cb){
        setTimeout(cb, ms);
    };
}
import fetch from 'node-fetch';

co(function* (){
    let result = yield [
        (yield fetch('https://api.github.com/users/1')).json(),
        (yield fetch('https://api.github.com/users/2')).json(),
    ];
    console.log("result:",result);
});
複製代碼

不管是延遲執行,仍是併發的從兩個接口獲取數據,generator均可以用同步的方式編寫異步代碼。

await,async(ECMAScript7)

ES7 引入了像C#語言中的 await,async關鍵字,並且babel已支持(引入plugins:transform-async-to-generator )

async函數徹底能夠看做多個異步操做,包裝成的一個Promise對象,而await命令就是內部then命令的語法糖。

import fetch from 'node-fetch';
(async function (){
    let result= await fetch('https://api.github.com/users/etoah');
    let json =await result.json();
    console.log("result:",json);
})();

//exception
(async function (){
    try{
        let result= await fetch('https://api.3github.com/users/etoah');
        let json =await result.json();
        console.log("result:",json);
    }
    catch(ex){
        console.warn("warn:",ex);
    }
})()
複製代碼

簡單比較會發現,async函數就是將Generator函數的星號(*)替換成async,將yield替換成await,同時不須要co模塊,更加語義化。

可是與yeild又不徹底相同,標準沒有接收await*的語法( :( 查看詳情),

若需「並行」執行promise數組,推薦用Promise.All,因此須要並行請求時,須要這樣寫:

(async function (){
    let result= await Promise.all([
         (await fetch('https://api.github.com/users/tj')).json(),
         (await fetch('https://api.github.com/users/etoah')).json()
        ]);
    console.log("result:",result);
})();
複製代碼

總結

到這裏,jser結合promise,yield,await的寫法,能夠和回調嵌套說拜拜了。

相關文章
相關標籤/搜索