JavaScript中async與await實(shí)現(xiàn)原理與細(xì)節(jié)
一、回調(diào)地獄
在es6興起之后許多人都開(kāi)始使用promise,promise目的是解決es5中的回調(diào)地獄(callback hell),那么什么是回調(diào)地獄呢?先來(lái)提一個(gè)需求,現(xiàn)在需要發(fā)送n個(gè)request請(qǐng)求,第二個(gè)請(qǐng)求參數(shù)需要第一個(gè)請(qǐng)求的結(jié)果,第三個(gè)請(qǐng)求的參數(shù)需要第二個(gè)請(qǐng)求的結(jié)果,以此類推... ,在沒(méi)有promise的條件下,我們不難使用callback寫(xiě)出如下的代碼:
function ajax(url, callback) {
setTimeout(() => {
callback(Math.random() + url)
}, 1000);
}
function request() {
ajax('url1', (res1 => {
ajax(`url2?random=${res1}`, (res2) => {
ajax(`url3?random=${res2}`, (res3) => {
ajax(`url4?random=${res3}`, (res4) => {
// do something
})
})
})
}))
}
request()二、Promise
這樣確實(shí)能實(shí)現(xiàn)我們的需求,但是這樣子的代碼有什么缺點(diǎn)呢?不難看出我們的request函數(shù)越來(lái)越像個(gè)三角形 ,代碼集中在上部分,下半部分全都是我們的括號(hào),代碼閱讀性極差! 這時(shí)候我們的promise應(yīng)運(yùn)而生了,使用promise我們可以這樣重構(gòu)我們的代碼如下:
function ajax(url) {
return new Promise(resolve => {
setTimeout(() => {
resolve(Math.random() + url)
}, 1000);
})
}
function request() {
ajax('url1').then(res1 => {
ajax(`url2?random=${res1}`).then((res2) => {
ajax(`url3?random=${res2}`).then((res3) => {
ajax(`url4?random=${res3}`).then((res4) => {
// do something
})
})
})
})
}
request()肯定有人說(shuō),這不還是像個(gè)三角形嗎?這樣使用promise有什么意義呢?此時(shí)我們可以借助promise的鏈?zhǔn)秸{(diào)用重構(gòu)成下面這樣:
function ajax(url) {
return new Promise(resolve => {
setTimeout(() => {
resolve(Math.random() + url)
}, 1000);
})
}
function request() {
ajax('url1').then(res1 => {
return ajax(`url2?random=${res1}`)
}).then(res2 => {
return ajax(`url3?random=${res2}`)
}).then(res3 => {
return ajax(`url4?random=${res3}`)
}).then(res4 => {
// do something
})
}
request()相對(duì)于之前的回調(diào)地獄,此時(shí)我們的代碼是不是比較清晰了。但是!這還不夠!這看上去還不夠直觀。我們想要的是閱讀異步代碼,類似于閱讀同步代碼的方式一樣方便。
三、生成器(generator)
生成器是es6新增的語(yǔ)法,它是一個(gè)特殊的迭代器,它可以用來(lái)暫停我們函數(shù)的執(zhí)行!這個(gè)功能非常強(qiáng)大! 生成器的語(yǔ)法是,在聲明函數(shù)時(shí)在后面增加一個(gè) * 號(hào),那么這個(gè)函數(shù)就是生成器函數(shù),直接調(diào)用該函數(shù)得到的是一個(gè)生成器句柄,該生成器是不會(huì)執(zhí)行的,必須要調(diào)用生成器句柄的next()方法后,生成器才會(huì)執(zhí)行,并且執(zhí)行到我們的yield處(如果存在yield就執(zhí)行到第一個(gè)yield,不存在則直接執(zhí)行完畢),該方法的返回值一個(gè)對(duì)象,結(jié)構(gòu)是 {done: true/false, value: 我們yield后面跟的值} ,如果執(zhí)行到該生成器函數(shù)末尾則 done為true。 關(guān)于生成器的知識(shí)可以點(diǎn)此JavaScript中的迭代器和可迭代對(duì)象與生成器
function* foo() {
console.log('======');
const a = yield 1;
console.log('a',a);
}
const g = foo()
console.log('11111111')
const res1 = g.next()
console.log(res1)
const res2 = g.next('22222')
console.log(res2)上面代碼打印順序?yàn)椋?/strong>
11111111
======
{done: false, value: 1}
'a','22222'
{done: true, value: undefined}
細(xì)心的你一定看出了,我們?cè)趎ext方法中傳的參數(shù)會(huì)賦值給生成器函數(shù)中的yield 左側(cè),并可以在生成器中拿到這個(gè)值后進(jìn)行使用。
四、使用生成器同步化promise
掌握了生成器的知識(shí)我們就可以使用生成器來(lái)將我們的promise鏈?zhǔn)秸{(diào)用進(jìn)行重構(gòu)如下:
function ajax(url) {
return new Promise(resolve => {
setTimeout(() => {
resolve(Math.random() + url)
}, 1000);
})
}
function* request() {
const res1 = yield ajax('url1')
const res2 = yield ajax(`url2?random=${res1}`)
const res3 = yield ajax(`url3?random=${res2}`)
const res4 = yield ajax(`url4?random=${res3}`)
// do something
console.log(res4);
}
// 開(kāi)始調(diào)用我們的生成器
const generator = request();
generator.next().value.then(res1 => {
generator.next(res1).value.then(res2 => {
generator.next(res2).value.then(res3 => {
generator.next(res3).value.then(res4 => {
generator.next(res4)
})
})
})
})可以看到我們的生成器還是三角形,優(yōu)化一下成鏈?zhǔn)秸{(diào)用如下:
generator.next().value.then(res1 => {
return generator.next(res1).value
}).then(res2 => {
return generator.next(res2).value
}).then(res3 => {
return generator.next(res3).value
}).then(res4 => {
generator.next(res4)
})此時(shí),我們的主函數(shù)已經(jīng)非常像同步代碼了,但是缺點(diǎn)是我們目前必須手動(dòng)調(diào)用該生成器,并且request主函數(shù)里面我們不知道有多少次yield調(diào)用,因此我們的生成器也不能手動(dòng)調(diào)用多次,這時(shí),我們將該生成器調(diào)用代碼進(jìn)行重構(gòu),重構(gòu)成可以自動(dòng)執(zhí)行我們的生成器的函數(shù),不需要關(guān)心request內(nèi)部有多少次yield使用,重構(gòu)如下:
function ajax(url) {
return new Promise(resolve => {
setTimeout(() => {
resolve(Math.random() + url)
}, 1000);
})
}
function* request() {
const res1 = yield ajax('url1')
const res2 = yield ajax(`url2?random=${res1}`)
const res3 = yield ajax(`url3?random=${res2}`)
const res4 = yield ajax(`url4?random=${res3}`)
// do something
console.log(res4);
}
function execGenerator(generatorFn) {
const generator = generatorFn();
function exec(res) {
const { done, value } = generator.next(res)
if (!done) {
value.then(exec)
}
}
exec()
}
execGenerator(request)我們?cè)黾恿艘粋€(gè)自動(dòng)執(zhí)行函數(shù)execGenerator,該函數(shù)接受一個(gè)生成器參數(shù),并且在內(nèi)部自動(dòng)進(jìn)行遞歸調(diào)用,直至返回值的 done 屬性為 true,此時(shí)我們的使用方式只需要定義一個(gè)request生成器函數(shù),并且執(zhí)行一下我們的自動(dòng)執(zhí)行函數(shù) execGenerator ,我們的request就能像同步代碼一樣盤跑起來(lái)了,并且看起來(lái)非常直觀。
五、async、await異步代碼究極解決方案
其實(shí)async與await是我們上面生成器的語(yǔ)法糖而已,在內(nèi)部做的事情跟我們使用生成器做的事情是一樣的,使用方式如下:
function ajax(url) {
return new Promise(resolve => {
setTimeout(() => {
resolve(Math.random() + url)
}, 1000);
})
}
async function request() {
const res1 = await ajax('url1')
const res2 = await ajax(`url2?random=${res1}`)
const res3 = await ajax(`url3?random=${res2}`)
const res4 = await ajax(`url4?random=${res3}`)
// do something
console.log(res4);
}看起來(lái)是不是跟我們的生成器request函數(shù)非常類似呢?使用async與await可以讓我們省去寫(xiě)execGenerator函數(shù)的步驟,更加方便了我們的開(kāi)發(fā)!
到此這篇關(guān)于JavaScript中async與await實(shí)現(xiàn)原理與細(xì)節(jié)的文章就介紹到這了,更多相關(guān)JS 中async與await 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解js正則表達(dá)式驗(yàn)證時(shí)間格式xxxx-xx-xx形式
本篇文章主要介紹了詳解js正則表達(dá)式驗(yàn)證時(shí)間格式xxxx-xx-xx形式,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-02-02
JavaScript中判斷整數(shù)的多種方法總結(jié)
這篇文章主要介紹了JavaScript中判斷整數(shù)的多種方法總結(jié),本文總結(jié)了5種判斷整數(shù)的方法,如取余運(yùn)算符判斷、Math.round、Math.ceil、Math.floor判斷等,需要的朋友可以參考下2014-11-11
javascript實(shí)現(xiàn)電商放大鏡效果
這篇文章主要為大家詳細(xì)介紹了javascript實(shí)現(xiàn)電商放大鏡效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-11-11
element-ui的表單驗(yàn)證清除校驗(yàn)提示語(yǔ)的解決方案
對(duì)表單域中的數(shù)據(jù)進(jìn)行校驗(yàn)的時(shí)候,其中有一項(xiàng)比較特殊,不是簡(jiǎn)單的輸入框,下拉框這些表單元素,而是自己寫(xiě)的一個(gè)el-table的選擇彈窗,本文給大家介紹element-ui的表單驗(yàn)證如何清除校驗(yàn)提示語(yǔ),感興趣的朋友一起看看吧2024-01-01
javascript實(shí)現(xiàn)簡(jiǎn)易計(jì)算器的代碼
下面小編就為大家?guī)?lái)一篇javascript實(shí)現(xiàn)簡(jiǎn)易計(jì)算器的代碼小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-05-05
JS獲取下拉列表所選中的TEXT和Value的實(shí)現(xiàn)代碼
本篇文章主要是對(duì)JS獲取下拉列表所選中的TEXT和Value的實(shí)現(xiàn)代碼進(jìn)行了介紹,需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2014-01-01

