理解ES7中的async/await
優勢是:就是解決多層非同步回撥的巢狀
從字面上理解 async/await, async是 "非同步"的含義,await可以認為是 async wait的簡寫,因此可以理解 async 用於宣告一個function是非同步的,而await用於等待一個非同步方法執行完成返回的值(返回值可以是一個Promise物件或普通返回的值)。
注意:await 只能出現在 async函式中。
1-1 async的作用?
首先來理解async函式是怎麼處理返回值的,我們以前寫程式碼都是通過return語句返回我們想要的值,但是async與return有什麼區別呢? 下面我們看如下程式碼:
async function testAsync() { return 'hello world'; } const result = testAsync(); console.log(result); // 列印出來看到 返回一個promise物件
如上程式碼,我們輸出的是一個Promise物件。因此 async返回的是一個Promise物件,因此我們可以使用 then()鏈來處理這個Promise物件。像如下程式碼:
async function testAsync() { return 'hello world'; } const result = testAsync(); result.then(v => { console.log(v); // 輸出 hello world });
但是如果 async函式沒有返回值的話,那麼就會返回 undefined; 如下程式碼:
async function testAsync() { return; } const result = testAsync(); result.then(v => { console.log(v); // 輸出 undefined });
1-2 await的作用?
從語義上說,await是在等待一個async函式完成的,async函式返回的是一個Promise物件,await等待的是一個表示式,這個表示式的計算結果是Promise物件或其他值。
async函式返回一個Promise物件,await作用是用於等待一個async函式的返回值。
await函式後面可以接普通函式呼叫或直接量,請看如下程式碼:
function getSomething() { return 'something'; } async function testAsync() { return Promise.resolve('hello'); } async function test () { const v1 = await getSomething(); const v2 = await testAsync(); console.log(v1); // something console.log(v2); // hello } test();
注意:await 是等待async函式返回的Promise物件或其他值,await是一個運算子,用於組成表示式, 如果等待的是一個Promise物件,await會阻塞後面的程式碼(async呼叫不會造成堵塞,它內部所有的堵塞
都被封裝在一個Promise物件中非同步執行),等待Promise物件的resolve,然後得到resolve的值,作為await表示式的運輸結果。
1-3 async/await 的一起使用的作用及優勢在哪?
我們先不用 async/await, 來看看使用setTimeout模擬非同步操作如下程式碼:
var test = function(time) { return new Promise((resolve, reject) => { setTimeout(function(){ resolve('hello world'); }, time) }); }; test(1000).then((v) => { console.log(v); });
如果改用 async/await 程式碼如下:
var testAsync = function(time) { return new Promise((resolve, reject) => { setTimeout(function(){ resolve('hello world'); }, time) }); }; async function test(time) { const v = await testAsync(time); console.log(v); // hello world } test(1000);
看上面的程式碼,反而會覺得 使用 async/await 程式碼變得多一點,複雜一點,但是結果貌似都一樣,那麼使用 async/await的優勢在哪?
1-4 async/await的優勢在於處理then鏈
單一的Promise鏈貌似不能發現 async/await的優勢,但是如果需要處理多個Promise組成的then鏈的時候,優勢可以看出來,因為Promise是通過then鏈來解決多層回撥的問題,現在我們又可以使用
async/await來進一步優化,他們的優點就是解決多層非同步回撥的巢狀。
假設我們現在有一個需求是,分多個步驟完成,每個步驟都是非同步的,並且後面的非同步都需要依賴於上一個非同步回撥返回的資料,進行往下傳遞。我們先用 setTimeout來模擬非同步操作。
function useTimeout (t) { return new Promise((resolve, reject) => { setTimeout(()=> { resolve(t+100) }, t); }); } function step1(t) { console.log(`step1 with ${t}`); // step1 with 300 return useTimeout(t); } function step2(t) { console.log(`step2 with ${t}`); // step2 with 400 return useTimeout(t); } function step3(t) { console.log(`step3 with ${t}`); // step3 with 500 return useTimeout(t); } function test() { const time1 = 300; step1(time1) .then(time2 => step2(time2)) .then(time3 => step3(time3)) .then(res => { console.log(`result is ${res}`); // result is 600 }) } test();
如果我們使用 async/await來實現,程式碼變為如下:
function useTimeout (t) { return new Promise((resolve, reject) => { setTimeout(()=> { resolve(t+100) }, t); }); } function step1(t) { console.log(`step1 with ${t}`); // step1 with 300 return useTimeout(t); } function step2(t) { console.log(`step2 with ${t}`); // step2 with 400 return useTimeout(t); } function step3(t) { console.log(`step3 with ${t}`); // step3 with 500 return useTimeout(t); } async function test() { const time1 = 300; const time2 = await step1(time1); const time3 = await step2(time2); const result = await step3(time3); console.log(`result is ${result}`); } test();
上面我們可以看到 使用async/await 程式碼看起來都是同步的,等第一步完成後,再執行第二步,依次類推..... 並且不需要更多的回撥函式巢狀。
下面我們再來看下我們之前講的,後面的步驟需要上一個步驟的結果傳遞進去,使用async/await的優勢可能更明顯。
如下程式碼:
function useTimeout (t) { return new Promise((resolve, reject) => { setTimeout(()=> { resolve(t+100) }, t); }); } function step1(t1) { console.log(`step1 with ${t1}`); // step1 with 300 return useTimeout(t1); } function step2(t1, t2) { console.log(`step2 with ${t1} and ${t2}`); // step2 with 300 and 400 return useTimeout(t1+t2); } function step3(t1, t2, t3) { console.log(`step3 with ${t1} and ${t2} and ${t3}`); // step3 with 300 and 400 and 800 return useTimeout(t1+t2+t3); } async function test() { const time1 = 300; const time2 = await step1(time1); const time3 = await step2(time1, time2); const result = await step3(time1, time2, time3); console.log(`result is ${result}`); // result is 1600 } test();
1-5 捕捉錯誤
我們可以使用 try catch 來捕獲錯誤,如下程式碼:
var sleep = function (time) { return new Promise((resolve, reject) => { setTimeout(function() { // 出錯了, 返回 error reject('error'); }, time); }) }; var start = async function() { try { console.log('start'); await sleep(3000); // 返回一個錯誤 // 下面程式碼不會被執行 console.log('end'); } catch (err) { console.log(err); // 捕捉錯誤 error } }; start();