ES6 Promise 和 Async/await的使用

你可能知道,Javascript語言的執行環境是"單線程"(single thread)。javascript

所謂"單線程",就是指一次只能完成一件任務。若是有多個任務,就必須排隊,前面一個任務完成,再執行後面一個任務,以此類推。java

這種模式的好處是實現起來比較簡單,執行環境相對單純;壞處是隻要有一個任務耗時很長,後面的任務都必須排隊等着,會拖延整個程序的執行。常見的瀏覽器無響應(假死),每每就是由於某一段Javascript代碼長時間運行(好比死循環),致使整個頁面卡在這個地方,其餘任務沒法執行。es6

爲了解決這個問題,Javascript語言將任務的執行模式分紅兩種:同步(Synchronous)和異步(Asynchronous)。編程

1.回調

回調是異步編程最基本的方法。api

假定有兩個函數f1和f2,後者等待前者的執行結果。promise

f1();
f2();
複製代碼

若是f1是一個很耗時的任務,能夠考慮改寫f1,把f2寫成f1的回調函數。瀏覽器

function f1(callback){
  setTimeout(function () {
    // f1的任務代碼
    callback();
  }, 1000);
}
複製代碼

執行代碼就變成下面這樣bash

f1(f2);
複製代碼

採用這種方式,咱們把同步操做變成了異步操做,f1不會堵塞程序運行,至關於先執行程序的主要邏輯,將耗時的操做推遲執行。 回調函數的優勢是簡單、容易理解和部署,缺點是不利於代碼的閱讀和維護,各個部分之間高度耦合,流程會很混亂,並且每一個任務只能指定一個回調函數。併發

2.Promise

Promises對象是CommonJS工做組提出的一種規範,目的是爲異步編程提供統一接口。異步

簡單說,它的思想是, 每個異步任務返回一個Promise對象,該對象有一個then方法,容許指定回調函數。 Promises的出現大大改善了異步變成的困境,避免出現回調地獄,嵌套層級獲得改善。

基本Api

  1. Promise.resolve()
  2. Promise.reject()
  3. Promise.prototype.then()
  4. Promise.prototype.catch()
  5. Promise.all() // 全部的完成
  6. Promise.race() // 競速,完成一個便可

具體api的介紹請看 阮一峯 大神的 ECMAScript 6 入門 在這我舉幾個簡單的場景的實現

模擬兩個異步請求

爲了使代碼簡介,promise的rejected狀態的相關reject()和catch()方法省略

// 1請求
  function getData1 () {
    return new Promise(function (resolve, reject) {
      setTimeout(() => {
        console.log('1執行了')
        resolve('請求到模擬數據1111拉')
      }, 2000)
    })
  }
  // 2請求
  function getData2 (params) {
    return new Promise(function (resolve, reject) {
      setTimeout(() => {
        console.log('2執行了')
        resolve('請求到模擬數據22222拉!params:' + params)
      }, 1500)
    })
  }
複製代碼

promise 實現異步回調 異步列隊

1請求完成後,把1的響應參數傳入2,在發2請求

function promiseDemo () {
    getData1()
      .then(res => {
        return getData2(res)
      })
      .then(res => {
        console.log(res)
      })
  }
  promiseDemo()
  // 1執行了
  // 2執行了
  // 請求到模擬數據22222拉!params:請求到模擬數據1111拉 用時 3500 ms
複製代碼

promise.all() 實現異步回調 併發 全部的完成

1請求、2請求同時發,兩條響應都收到後在執行

function promiseDemo () {
    Promise.all([getData1(), getData2()]).then(function (res) {
      console.log(res)
    })
  }
  // 2執行了
  // 1執行了
  // ["請求到模擬數據1111拉", "請求到模擬數據22222拉!params:undefined"] 用時 2000 ms
複製代碼

promise.race() 實現異步回調 併發 競速

1請求、2請求同時發,其中一條收到請求就執行

function promiseDemo () {
    Promise.race([getData1(), getData2()]).then(function (res) {
      console.log(res)
    })
  }
  // 2執行了
  // 請求到模擬數據22222拉!params:undefined 用時 1500 ms
  // 1執行了 
複製代碼

由此Promise對象仍是很好用的,對於異步的流程的控制獲得了大大改善,經過.then()的方法可進行鏈式調用。 但是 .then() .catch() 的使用也致使代碼很是難看,嵌套也很深,因此async/await就出來了

Async/await

Async/await 是Javascript編寫異步程序的新方法。以往的異步方法無外乎回調函數和Promise。可是Async/await創建於Promise之上。

如何使用 Async 函數

咱們仍是來看一 看阮一峯 大神的 ECMAScript 6 入門 的例子

async function timeout(ms) {
  await new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

async function asyncPrint(value, ms) {
  await timeout(ms);
  console.log(value);
}

asyncPrint('hello world', 50);
複製代碼

上面代碼指定50毫秒之後,輸出hello world。 進一步說,async函數徹底能夠看做多個異步操做,包裝成的一個 Promise 對象,而await命令就是內部then命令的語法糖

咱們看具體的示例

async 實現異步回調 異步列隊

1請求完成後,把1的響應參數傳入2,在發2請求

上文中的promise 實現方法是經過then的鏈式調用,可是採用async會更加簡潔明瞭

async function asyncDemo () {
    const r1 = await getData1()
    const r2 = await getData2(r1)
    console.log(r2)
  }
  // 1執行了
  // 2執行了
  // 請求到模擬數據22222拉!params:請求到模擬數據1111拉 用時 3500 ms
複製代碼

用同步的書寫方式實現了異步的代碼。等待getData1的異步函數執行完了後發返回值賦值給r1,傳入r2,在執行r2

async 異步回調 併發

1請求、2請求同時發,規定請求到達的順序

假如咱們有一種這樣的業務需求,併發兩個請求,可是要規定收到請求的順序應該怎麼作的?這裏仍是借鑑阮一峯大神的代碼

async function asyncDemo2 () {
    const arr = [getData1, getData2]
    const textPromises = arr.map(async function (doc) {
      const response = await doc()
      return response
    })
    // 按次序輸出
    for (const textPromise of textPromises) {
      console.log(await textPromise);
    }
  }
  // 2執行了 (由於2是 1500ms後執行) 因此2先執行
  // 1執行了
  // 請求到模擬數據1拉 (for .. of )規定了輸出的順序
  // 請求到模擬數據22222拉!params:undefined
複製代碼

面代碼中,雖然map方法的參數是async函數,但它是併發執行的,由於只有async函數內部是繼發執行,外部不受影響。後面的for..of循環內部使用了await,所以實現了按順序輸出

怎麼樣這麼BT的需求都能實現把。

async 總結

他使得異步代碼變的再也不明顯也是一點弊端咯,不過根據實際狀況選擇最合適的異步編程纔是最好的選擇。async 是 Generator 函數的語法糖。因此想更深刻的理解其中內部原理的趕忙去看看 Generator 函數把

相關文章
相關標籤/搜索