聊聊async與await

工做中一直沒有實際用過async和await,可是做爲一個前端仍是儘可能不要讓本身有什麼盲區前端

ok 咱們來研究一下吧node

一、先看看怎麼用

function add(num) {
    return new Promise(resolve => {
      setTimeout(() => {
        resolve(num + 1);
      }, 2000);
    });
  }
  async function test() {
    console.log('test');
    var result = await add(1);
    console.log(result); //2秒後輸出
  }
  test();
注意: await 關鍵字僅僅在 async function中有效。若是在 async function函數體外使用 await ,你只會獲得一個語法錯誤 Uncaught SyntaxError: await is only valid in async function

二、看看async這是啥

async function testAsync() {
    return "hello async";
}

const result = testAsync();
console.log(result);  // Promise { 'hello async' }

看到輸出就恍然大悟了——輸出的是一個 Promise 對象。es6

因此,async 函數返回的是一個 Promise 對象。從文檔中也能夠獲得這個信息。async 函數(包含函數語句、函數表達式、Lambda表達式)會返回一個 Promise 對象,若是在函數中 return 一個直接量,async 會把這個直接量經過 Promise.resolve() 封裝成 Promise 對象。瀏覽器

async 函數返回的是一個 Promise 對象,因此在最外層不能用 await 獲取其返回值的狀況下,咱們固然應該用原來的方式:then() 鏈來處理這個 Promise 對象,就像這樣異步

testAsync().then(v => {
    console.log(v);    // 輸出 hello async
});

如今回過頭來想下,若是 async 函數沒有返回值,又該如何?很容易想到,它會返回 Promise.resolve(undefined)。async

聯想一下 Promise 的特色——無等待,因此在沒有 await 的狀況下執行 async 函數,它會當即執行,返回一個 Promise 對象,而且,毫不會阻塞後面的語句。這和普通返回 Promise 對象的函數並沒有二致。函數

那麼下一個關鍵點就在於 await 關鍵字了。優化

三、那await是什麼鬼

看看下面這段代碼code

// ps:因爲js自己如今已經限制了await必須用在async函數中,不然會報錯。因此請將下面的複製粘貼到瀏覽器控制檯查看結果

function asyncFn () {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      if (true) {
        console.log('resolve console')
        resolve('resolve return')
      } else {
        reject('reject return')
      }
    }, 2000)
  })
}

var value1 = await asyncFn()
var value2 = await 'plain text'
console.log(value1)
console.log(value2)

//瀏覽器會依次打印 ‘resolve console’ ‘resolve return’ ‘plain text’
觀察他們的打印的時間和順序就能看出來,await讓咱們的代碼阻塞了!爲了避免讓代碼阻塞,瀏覽器要求必需要在async中使用await

那爲何在控制檯就不會報錯呢?答:阻塞只是一個提案,谷歌控制檯支持對象

ok 能夠看出來,async是異步,await就是等待

async+await用一句白話一點說就是讓代碼在async方法中阻塞(或叫同步)

四、async/await存在的意義又是什麼呢?

上面已經說明了 async 會將其後的函數(函數表達式或 Lambda)的返回值封裝成一個 Promise 對象,而 await 會等待這個 Promise 完成,並將其 resolve 的結果返回出來。

如今舉例,用 setTimeout 模擬耗時的異步操做,先來看看不用 async/await 會怎麼寫

function takeLongTime() {
    return new Promise(resolve => {
        setTimeout(() => resolve("long_time_value"), 1000);
    });
}

takeLongTime().then(v => {
    console.log("got", v);
});

若是改用 async/await 呢,會是這樣

function takeLongTime() {
    return new Promise(resolve => {
        setTimeout(() => resolve("long_time_value"), 1000);
    });
}

async function test() {
    const v = await takeLongTime();
    console.log(v);
}

test();

眼尖的同窗已經發現 takeLongTime() 沒有申明爲 async。實際上,takeLongTime() 自己就是返回的 Promise 對象,加不加 async 結果都同樣,若是沒明白,請回過頭再去看看上面的「async 起什麼做用」。

又一個疑問產生了,這兩段代碼,兩種方式對異步調用的處理(實際就是對 Promise 對象的處理)差異並不明顯,甚至使用 async/await 還須要多寫一些代碼,那它的優點到底在哪?

async/await 的優點在於處理 then 鏈
單一的 Promise 鏈並不能發現 async/await 的優點,可是,若是須要處理由多個 Promise 組成的 then 鏈的時候,優點就能體現出來了(頗有意思,Promise 經過 then 鏈來解決多層回調的問題,如今又用 async/await 來進一步優化它)。

假設一個業務,分多個步驟完成,每一個步驟都是異步的,並且依賴於上一個步驟的結果。咱們仍然用 setTimeout 來模擬異步操做:

/**
 * 傳入參數 n,表示這個函數執行的時間(毫秒)
 * 執行的結果是 n + 200,這個值將用於下一步驟
 */
function takeLongTime(n) {
    return new Promise(resolve => {
        setTimeout(() => resolve(n + 200), n);
    });
}

function step1(n) {
    console.log(`step1 with ${n}`);
    return takeLongTime(n);
}

function step2(n) {
    console.log(`step2 with ${n}`);
    return takeLongTime(n);
}

function step3(n) {
    console.log(`step3 with ${n}`);
    return takeLongTime(n);
}

如今用 Promise 方式來實現這三個步驟的處理

function doIt() {
    console.time("doIt");
    const time1 = 300;
    step1(time1)
        .then(time2 => step2(time2))
        .then(time3 => step3(time3))
        .then(result => {
            console.log(`result is ${result}`);
            console.timeEnd("doIt");
        });
}

doIt();

// c:\var\test>node --harmony_async_await .
// step1 with 300
// step2 with 500
// step3 with 700
// result is 900
// doIt: 1507.251ms

輸出結果 result 是 step3() 的參數 700 + 200 = 900。doIt() 順序執行了三個步驟,一共用了 300 + 500 + 700 = 1500 毫秒,和 console.time()/console.timeEnd() 計算的結果一致。

若是用 async/await 來實現呢,會是這樣

async function doIt() {
    console.time("doIt");
    const time1 = 300;
    const time2 = await step1(time1);
    const time3 = await step2(time2);
    const result = await step3(time3);
    console.log(`result is ${result}`);
    console.timeEnd("doIt");
}

doIt();

結果和以前的 Promise 實現是同樣的,可是這個代碼看起來是否是清晰得多,幾乎跟同步代碼同樣

ok 這就是async/await啦,聊了聊這些,不由讓我想起es6的Generator

哈哈哈哈,技術無止境呀,以後咱們再整理Generator的博客吧~

相關文章
相關標籤/搜索