網上有不少關於async/await的學習文章,我也是經過這些文章學習瞭解async的,可是不總結一下,總以爲沒有真正學到。若有錯誤的地方還請多多指出。javascript
有人說它是JS編程異步的「終極解決方案」,終不終極不知道,事物老是在發展的嘛,可是能被冠上這麼個頭銜也絕非等閒之輩了😊。async是「異步的」,await是「等待」,async是形容詞,await是動詞,那麼async形容的函數告訴別人這是個包含異步的「異步的函數」,而await要等待某個東西的到來。a/a給了咱們從新使用try catch的權力,而且規定await必須寫在async形容的函數中。使用a/a要對Promise有所理解,那還要從回調提及。html
JS是一根筋的(單線程),一次只執行一個任務,後面的要等前面的完成才行, 若是某個任務要佔用很長時間,那麼後面的任務就都要等着。因此java
JS將任務的執行模式分紅兩種:同步(Synchronous)和異步(Asynchronous)。 地址
回調函數就是「異步「的方法之一。你們對回調很熟啦,回調會產生一個問題-回調地獄,兩層回調還好,三層彷佛也能夠接受,硬着頭皮四層也行,五層要不要寫,咬咬牙六層就行了。。。最後,一首楊宗緯的洋蔥送給本身。一層一層剝開回調,找到最終的小可愛。
好比下面的栗子:編程
a1(function(res) { d2(res, function(newRes) { a3(newRes, function(finalRes) { console.log('a3: ' + finalRes); }, a1FCb); }, a2FailCb); }, a3FailCb);
另外,當年我還年輕的時候,初試回調,在回調函數中花樣百出的嘗試return
,在回調外面也取不到return的值,大概下面這個樣子promise
var a = '' function fool(){ setTimeout(() => { return 'hi'// 或者 }, 1000); } a = fool() console.log(a) //undefined 由於setTimeout異步執行,a=fool()執行在timeout以前,而此時的fool()並內有return任何東西
回調讓咱們失去了直接「使用return」的方式,那來看看Promise。異步
顧名思義,promise是承諾的意思,承諾在將來是能夠兌現的,promise能夠有效的解決回調地獄,並且給了咱們使用return的權力。只不過它的return還是一個承諾。對於promise的學習可參考promise MDN和Promise MDN。async
一個Promise
有如下幾種狀態:
pending: 初始狀態,既不是成功,也不是失敗狀態。
fulfilled: 意味着操做成功完成。
rejected: 意味着操做失敗。
以下一個利用Promise構造函數聲明的實例ide
new Promise(function(resolve,reject){//...})
resolve 和 reject 函數被調用時,分別將promise的狀態改成fulfilled(完成)或rejected(失敗),這兩個狀態不能同時出現,兩個狀態完成時,分別調用promise的then方法中對應的onfulfilled 和onrejected 函數。舉個例子:函數
function a(val){ return new Promise(function(resolve,reject){ if(val === 1){ resolve(val) } else { reject('bad val') } }) }
函數a接收一個參數,返回一個promise實例,當參數是指望的1時,表示操做成功,其餘值被認爲操做失敗。那麼若是執行如下操做學習
a(1).then(function(res){ console.log('onfulfilled '+ res) },function(errMsg){ console.log('onrejected '+ errMsg) }) //會log出'onfulfilled 1'
該操做a(1)
會調用函數a內部promise實例的resolve函數,把該promise的狀態變成了fulfilled,因此以後會調用then
函數裏onfulfilled的函數。
另外,對於操做失敗的狀態,除了在then中指定對應的onreject函數外,也可使用catch
,使用catch的好處在於,若是是多個異步操做,只須要指定一個catch便可,不用爲每層then都指定一個onreject函數。舉個例子:
有以下三層回調:
fun1(function(res1){ fun2(function(res1){ var res2 = res1 + 'hello' fun3(function(res2){ var res3 = res2 + 'hi' console.log('the final res ' + res3) },fun3FailCb) },fun2FailCb) },fun1FailCb)
改爲promise寫法後:
fun1() .then(function(res1){ return fun2(res1+'hello') }) .then(function(res2){ return fun3(res2+'hi') }) .then(function(res3){ console.log('the final res' + res3) }) .catch(function(err){ console.log('something bad occured') })
清爽不少。then的鏈寫多了,看起來也很煩啊,有沒有更簡潔的方法?有!
async/await
是一個語法糖,他和promise關係緊密,可參考async MDN。
當調用一個 async 函數時,會返回一個 Promise 對象。當這個 async 函數返回一個值時,Promise 的 resolve 方法會負責傳遞這個值;當 async 函數拋出異常時,Promise 的 reject 方法也會傳遞這個異常值。
回到開篇說的,async所形容的函數告訴別人這是個包含異步的「異步的函數」,也就是async的函數會返回一個promise (同步的代碼也不要緊,只不過沒啥意義了)。await要等的就是其餘async函數返回的這個promise,promise狀態成功,await等到的是resolve中的值,promise狀態失敗,await等到的是reject中的錯誤信息,換句話說,被try/catch中的catch到了。舉個例子:
async function f1(val){ if(val === 1){ return val } else { throw 'not 1' } } //調用f1(val)時,會自動返回一個包裝好的promise async function f2(val2){ try{ var res1 = await f1(val2) console.log('res1' , res1) }catch(errmsg){ console.log('catch ', errmsg) } }
分別執行f2(1)和f2(3)查看log結果。
改寫前面的三層回調
async function fun1(){ return res1 } async function fun2(val){ return val + 'hello' } async function fun3(val){ return val + 'hi' } async function fun4(){ try{ var res1 = await fun1() var res2 = await fun2(res1) var res3 = await fun3(res2) console.log('the final res ', res3) // other operation with res3/res2/res1... }catch(err){ console.log('something bad') } }
至關清爽。在async內,await會阻塞後面的程序執行,直到promise的狀態完成(成功或失敗)。