一、什么是async
async其實是ES7的才有的關鍵字,放在這里說,其實是和我們前面所說的Promise,Generator有很大關聯的。async的意思是"異步",顧名思義是有關異步操作有關的關鍵字。下面我們就來構造一個async方法。
async function helloAsync(){ return "helloAsync"; } console.log(helloAsync())//Promise {<resolved>: "helloAsync"}
申明async方法比較簡單,只需要在普通的函數前加上"async"關鍵字即可。我們執行下這個函數,發現並沒有返回字符串"helloAsync",而是通過Promise.resolved()將字符串封裝成了一個Promise對象返回。
既然是返回的Promise對象,我們就是用then方法來處理。
async function helloAsync(){ return "helloAsync"; } helloAsync().then(v=>{ console.log(v);//"helloAsync" })
到這,道友們可能納悶了,就是封裝一個Promise的對象返回,這有個毛用啊。別急,await關鍵字閃亮登場。
二、await關鍵字
在Generator章節中我們熟悉了yield關鍵字,yield關鍵字只能使用在Generator函數中,同樣,await關鍵字也不能單獨使用,是需要使用在async方法中。 await字面意思是"等待",那它是在等什么呢?它是在等待后面表達式的執行結果。
function testAwait(){ return new Promise((resolve) => { setTimeout(function(){ console.log("testAwait"); resolve(); }, 1000); }); } async function helloAsync(){ await testAwait(); console.log("helloAsync"); } helloAsync();
我們來分析下這段代碼
1、testAwait()方法中new一個Promise對象返回,promise對象中用setTimeout模擬一個異步過程,即1s后打印"testAwait"。
2、helloAsync()方法中,await testAwait(),表示將阻塞這里,等待testAwait這個異步方法執行並返回結果后,才繼續下面的代碼。
執行下,1s后打印了下面的日志。
到此,道友們是不是理解了await的作用,就是阻塞主函數的執行,直到后面的Promise函數返回結果。
聰明的道友可能要問,await后面只能 是Promise對象么?答案是否定的,可以是字符串,布爾值,數值以及普通函數。
function testAwait(){ setTimeout(function(){ console.log("testAwait"); }, 1000); } async function helloAsync(){ await testAwait(); console.log("helloAsync"); } helloAsync();
執行結果:
方法沒有報錯,說明await后面是支持非Promise函數的,但是執行的結果是不一樣的,所以await針對所跟的表達式不同,有兩種處理方式:
1、對於Promise對象,await會阻塞主函數的執行,等待 Promise 對象 resolve,然后得到 resolve 的值,作為 await 表達式的運算結果,然后繼續執行主函數接下來的代碼。
2、對於非Promise對象,await等待函數或者直接量的返回,而不是等待其執行結果。
我們知道Promise對象有兩種狀態,除了resolved,還有rejected,我們來看下如果promise對象變為rejected,會如何處理。
function testAwait(){ return Promise.reject("error"); } async function helloAsync(){ await testAwait(); console.log("helloAsync");//沒有打印 } helloAsync().then(v=>{ console.log(v); }).catch(e=>{ console.log(e);//"error" });
從執行結果看,返回reject狀態被外層的catch捕獲到,然后終止了后面的執行。
但是在有些情況下,出錯后是希望繼續執行,而不是中斷。對於這種情況可以采用tcy...catch在函數內部捕獲異常。
function testAwait(){ return Promise.reject("error"); } async function helloAsync(){ try{ await testAwait(); }catch(e){ console.log("this error:"+e)//this error:error } console.log("helloAsync");//helloAsync } helloAsync().then(v=>{ }).catch(e=>{ console.log(e);//沒有打印 });
異常被try...catch捕獲后,繼續執行下面的代碼,沒有導致中斷。
三、應用場景
上面說到,await可以阻塞主函數,直到后面的Promise對象執行完成。這個特性就能很輕松的解決按順序控制異步操作,即我們前一章節講的異步流程的問題。
道友們還記得在Generator章節的肚包雞的制作過程的實例,我們用async/await來重寫這個例子,並比較下兩者實現的區別。
//准備 function prepare(){ return new Promise((resolve) => { setTimeout(function(){ console.log("prepare chicken"); resolve(); },500) }); } //炒雞 function fired(){ return new Promise((resolve) => { setTimeout(function(){ console.log("fired chicken"); resolve(); },500) }); } //燉雞 function stewed(){ return new Promise((resolve) => { setTimeout(function(){ console.log("stewed chicken"); resolve(); },500) }); } //上料 function sdd(){ return new Promise((resolve) => { setTimeout(function(){ console.log("sdd chicken"); resolve(); },500) }); } //上菜 function serve(){ return new Promise((resolve) => { setTimeout(function(){ console.log("serve chicken"); resolve(); },500) }); } async function task(){ console.log("start task"); await prepare(); await fired(); await stewed(); await sdd(); await serve(); console.log("end task"); } task();
這段代碼看上去神清氣爽,我們來分析下代碼:
1、首先每個制作異步過程封裝成Promise對象。
2、利用await阻塞原理,實現每個制作的順序執行。
相比較Generator實現,無需run流程函數,完美的實現了異步流程。
四、總結
從Promise到Generator,再到async,對於異步編程的解決方案越來越完美,這就是ES6不斷發展的魅力所在。
---------------------
作者:恰恰虎
來源:CSDN
原文:https://blog.csdn.net/tcy83/article/details/80544048
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!