你一定要先理解以下重點(diǎn)內(nèi)容:
你要把 JS 里執(zhí)行分為兩種叔遂。
第一種是實(shí)打?qū)嵄仨氂?JS 線程執(zhí)行的,比如做一些去算一個(gè)文件的 MD5 值争剿,這種一般沒(méi)人寫異步已艰,沒(méi)好處,都是同步計(jì)算蚕苇。
第二種不是由 JS 線程去執(zhí)行哩掺,只是 JS 把任務(wù)提交給它,然后傻了吧唧的啥也不干等它完成并返回結(jié)果給自己涩笤。比如你調(diào)用瀏覽器接口去網(wǎng)絡(luò)上下載圖片嚼吞,此時(shí)你的 JS 線程是把任務(wù)提交給了瀏覽器,然后一直等瀏覽器把結(jié)果返回給你蹬碧。干活的是瀏覽器不是你 JS 線程舱禽。
對(duì)于不干活的情況,就得用異步的寫法了恩沽,不然輕則執(zhí)行效率低下誊稚,重則頁(yè)面卡死,畢竟頁(yè)面上那么多事等著 JS 線程去干罗心,特別是現(xiàn)在的網(wǎng)站內(nèi)容都是靠 JS 動(dòng)態(tài)加載的片吊。
以前使用的方法是回調(diào),就是調(diào)用接口時(shí)同時(shí)傳一個(gè)函數(shù)协屡,告訴瀏覽器或誰(shuí)執(zhí)行完任務(wù)時(shí)調(diào)用函數(shù)俏脊,然后我們?cè)诤瘮?shù)中放一些需要執(zhí)行完任務(wù)才能處理的事情。
比如讓瀏覽器去下圖片肤晓,我們就把顯示圖片放到函數(shù)中爷贫,這個(gè)函數(shù)就被稱為回調(diào)函數(shù)认然。你們常見(jiàn)的什么 onload
、onready
都是這個(gè)意思漫萄。
function download(callback) {
// 這個(gè)下載花費(fèi) 5 秒鐘
let file = "我是文件哦"
setTimeout(() => callback(file), 5000)
}
console.log("開(kāi)始執(zhí)行了~")
console.log("先去下個(gè)圖片~")
// 下載好了通知我就行卷员,不等你了
download((file) => {
console.log("下載完成:" + file)
console.log("顯示圖片")
})
console.log("做其他事情去了~")
// 開(kāi)始執(zhí)行了~
// 先去下個(gè)圖片~
// 做其他事情去了~
// 5 秒后......
// 下載完成:我是文件哦
// 顯示圖片
但假如,我要下個(gè)文件腾务,他的下載鏈接保存在 A文件里毕骡, A文件的下載鏈接保存在 B文件里, B文件的下載鏈接保存在 C文件里岩瘦。
那我必須先下C文件未巫,等C下完了拿到B的鏈接下B文件,再等B下完了去下A文件启昧,最后才能在A文件里拿到我的文件下載鏈接叙凡。(沒(méi)有這么蠢的場(chǎng)景,但多個(gè)任務(wù)依次依賴前一個(gè)任務(wù)完成才能處理的情況還是很常見(jiàn)的)
這樣就會(huì)形成多層嵌套的回調(diào)密末,就被稱為回調(diào)地獄握爷。
function download(callback) {
// 這個(gè)下載花費(fèi) 5 秒鐘
let file = "我是文件哦"
setTimeout(() => callback(file), 5000)
}
console.log("開(kāi)始執(zhí)行了~")
console.log("先去下個(gè)文件~")
// 下載好了通知我就行,不等你了
download((file) => {
console.log("下載 C 完成")
console.log("拿到 B 的鏈接")
download((file) => {
console.log("下載 B 完成")
console.log("拿到 A 的鏈接")
download((file) => {
console.log("下載 A 完成")
console.log("拿到 最終文件 的鏈接")
download((file) => {
console.log("下載 最終文件 完成")
})
})
})
})
console.log("做其他事情去了~")
于是乎严里,async + await 就出現(xiàn)了新啼,它可以讓我們用看起來(lái)像同步的方式去寫異步的程序。
對(duì)于 async 和 await 千萬(wàn)不要恐怖刹碾,你把它們理解成回調(diào)的語(yǔ)法糖就行了燥撞,而回調(diào)函數(shù)的內(nèi)容就是 await 之后的部分。
第一段的代碼就可以改寫成:
async function download() {
// 這個(gè)下載花費(fèi) 5 秒鐘
return new Promise((resolve, reject) => {
let file = "我是文件哦"
setTimeout(() => resolve(file), 5000)
})
}
async function showImage() {
let file = await download()
// 以下是之前回調(diào)函數(shù)的內(nèi)容
console.log("下載完成:" + file)
console.log("顯示圖片")
}
console.log("開(kāi)始執(zhí)行了~")
console.log("先去下個(gè)圖片~")
showImage()
console.log("做其他事情去了~")
// 開(kāi)始執(zhí)行了~
// 先去下個(gè)圖片~
// 做其他事情去了~
// 5 秒后......
// 下載完成:我是文件哦
// 顯示圖片
不過(guò)乍看之下好像還略微復(fù)雜了一點(diǎn)教硫,但如果改寫第二個(gè)程序呢?
async function download() {
// 這個(gè)下載花費(fèi) 5 秒鐘
return new Promise((resolve, reject) => {
let file = "我是文件哦"
setTimeout(() => resolve(file), 5000)
})
}
async function getFile() {
let file = await download()
console.log("下載 C 完成")
console.log("拿到 B 的鏈接")
file = await download()
console.log("下載 B 完成")
console.log("拿到 A 的鏈接")
file = await download()
console.log("下載 A 完成")
console.log("拿到 最終文件 的鏈接")
file = await download()
console.log("下載 A 完成")
console.log("下載 最終文件 完成")
}
console.log("開(kāi)始執(zhí)行了~")
console.log("先去下個(gè)圖片~")
getFile()
console.log("做其他事情去了~")
回調(diào)地獄不見(jiàn)了辆布,我們只用一個(gè)函數(shù)就完成了所有的內(nèi)容瞬矩,代碼真是太清爽了,async + await 真是太厲害了锋玲。