我們知道删性,javascript是單線程的亏娜,任務只能依次排開,蹬挺,挨個執(zhí)行维贺,但是為了完成某些并行的需求,類似網(wǎng)絡請求(網(wǎng)絡請求有單獨的線程去處理)巴帮,就必須引入異步的概念溯泣,簡單來理解就是:我想要吃飯,但是做好飯需要十分鐘榕茧,這十分鐘我不能待著垃沦,我要玩手機,等飯做好了用押,我再繼續(xù)吃飯肢簿。
最傳統(tǒng)普遍的異步解決方案就是寫回調函數(shù),
定時器
setTimeout(function(){
console.log("開始吃飯")
},600000)
網(wǎng)絡請求
$post("www.chifan.com",function(res){
console.log("吃飯")
})
這會產生幾個問題
回調地獄蜻拨,難處理的異常译仗,非常不優(yōu)雅的代碼。官觅。
隨著前端應用復雜性的不斷提升,回調函數(shù)越來越不能滿足我們的需求阐污,幾種解決方案被提了出來休涤,
Promise
,Generator
,async/await
Promise
new Promise(function(resolve,reject){
if("做好飯"){
resolve("飯好了")
}
else{
reject("飯糊了")
}
})
.then(res=>{
console.log("吃飯")
})
.catch(err=>{
console.log("吃不成了")
})
Generator
function* chifan(){
yeild 'chifan'
}
new chifan().next()
async/await
async function(){
await zuofan();
return "吃飯了"
}
下面我們著重來介紹下promise,因為promise + async await 是業(yè)界異步解決方案最優(yōu)雅的解法了笛辟,大約只有c# 和 nodejs 原生實現(xiàn)了此特性
我們先看下PromiseA+規(guī)范
- "promise"是一個函數(shù)功氨,攜帶有滿足規(guī)范的then方法
- "promise" 有三種狀態(tài),
pending
手幢,onFulfilled
捷凄,onRejected
,且從pending只能切換到onFulfilled或者onRejected一種狀態(tài)围来,不能切換 - then方法返回的也是promise跺涤,且可以鏈式調用
看懂了規(guī)范我們先來演示一下在promise 和 async await下我們如何解決異步問題。
比如我們想要在兩秒之后打印吃飯监透,吃完飯三秒后打印睡覺桶错,傳統(tǒng)回調寫法
setTimeout(function(){
console.log('吃飯');
setTimeout(function(){
console.log('睡覺')
},3000)
},2000)
promise 寫法
new Promise(function(res,rej){
setTimeout(function(){
console.log("吃飯")
res()
},2000)
})
.then(res=>{
return new Promise(function(res,rej){
setTimeout(function(){
console.log("睡覺")
res()
},3000)
})
})
.then(res=>{
},
reject=>{
console.log("異常")
})
.catch(err=>{
console.log(err)
})
async await 寫法
(async function(){
await new Promise(function(res,rej){
setTimeout(function(){
console.log("吃飯")
res()
},2000)
})
await new Promise(function(res,rej){
setTimeout(function(){
console.log("睡覺")
res()
},3000)
})
})()
是不是感覺越寫越像同步呢。
async 關鍵字用于匿名函數(shù)前面或者函數(shù)名字前面胀蛮,有了這個關鍵字院刁,函數(shù)內部可以使用await關鍵字,await關鍵字后面跟著一個promise對象粪狼,可以是函數(shù)返回一個promsie對象或者new 的一個promise對象退腥,而每個await對應promise之后的代碼任岸,都會在當前await對應的promise resolve 之后才會執(zhí)行,相當于將當前await 對應promise之后的代碼包裹到了then 之中執(zhí)行享潜,,然后如果又碰到await米碰。還是同理,一層層包裹吕座,保證了多個await 對應promise的順序執(zhí)行
現(xiàn)在async中的代碼可以順序執(zhí)行了,但是當前加了async 關鍵字的函數(shù)還是無法保證其他的函數(shù)在此函數(shù)執(zhí)行完之后執(zhí)行瘪板,因為瀏覽器的事件循環(huán)機制中吴趴,promise是放在微任務隊列中執(zhí)行的,侮攀,
因此,async 函數(shù)的返回值也是一個promise撇叁,無法保證當前async 函數(shù)后面的函數(shù)在此函數(shù)所有內容執(zhí)行完之后執(zhí)行,如果多個async 函數(shù)并行執(zhí)行畦贸,我們還是需要將它當成promsie 進行處理陨闹。
也就是說 async 的返回值是一個promise
這時候如果我們在es5 提供的數(shù)組的遍歷函數(shù)中使用async await 會產生一些難以預料的問題,如果你對async await promise 非常了解了薄坏,可以在 forEach
趋厉,map
胶坠, filter
等中使用,類似這種
[1,2,3].map(async _=>{
await new Promise();
return _+1
})
// 執(zhí)行其他代碼
這個時候的函數(shù)執(zhí)行時序會有些復雜沈善,如果你理解了本文,可以預見到執(zhí)行的結果闻牡,
如果你不是非常明白他的執(zhí)行時序,建議遇到循環(huán)時澈侠,還是乖乖的寫for
循環(huán)吧。。写妥。