工做中一直沒有實際用過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 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 關鍵字了。優化
看看下面這段代碼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 會將其後的函數(函數表達式或 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的博客吧~