同步和異步
眾所周知, JavaScript
是一門單線程的語(yǔ)言, 單線程就意味著同一時(shí)間只能執(zhí)行一個(gè)任務(wù), 當(dāng)前任務(wù)執(zhí)行結(jié)束, 才會(huì)執(zhí)行下一個(gè)任務(wù). 這種模式的好處就是執(zhí)行環(huán)境比較單純, 但壞處也很明顯, 一旦有某個(gè)任務(wù)卡住了, 就會(huì)導(dǎo)致整個(gè)程序阻塞. 為了解決這個(gè)問(wèn)題, JS將任務(wù)的執(zhí)行模式分成了同步(Synchronous)
和異步(Asynchronous)
兩種.
更詳細(xì)的同步異步的執(zhí)行模式可以參考事件循環(huán)
下面我們就來(lái)講講JS異步編程的幾種解決方案.
1.回調(diào)函數(shù)
回調(diào)函數(shù)是異步編程的最基本的方法, 它通常作為一個(gè)參數(shù)傳遞給另一個(gè)函數(shù), 并且在父函數(shù)執(zhí)行完成后執(zhí)行回調(diào)函數(shù)
例如:
function logB(){
console.log("b");
}
function parent(a, callback) {
console.log(a);
typeof callback === 'function' ? callback() : return
}
parent('a', logB)
在上面的例子中, 在parent
函數(shù)中, 如果callback參數(shù)傳入的是一個(gè)函數(shù), 那么在輸出形參a
之后就會(huì)執(zhí)行callback
函數(shù), 上述例子中l(wèi)ogB就是作為回調(diào)函數(shù)傳入parent中并執(zhí)行
在實(shí)際開(kāi)發(fā)過(guò)程中, 回調(diào)函數(shù)的使用場(chǎng)景還是很多的, 例如常見(jiàn)的數(shù)組遍歷方法forEach
, 其第一個(gè)參數(shù)就是回調(diào)函數(shù)
let arr = [1, 2, 3]
arr.forEach((item) => {
console.log(item);
})
回調(diào)函數(shù)的優(yōu)點(diǎn)是簡(jiǎn)單易懂, 但其實(shí)一旦嵌套起來(lái)很不利于代碼的維護(hù)和可讀性. 而且各個(gè)部分之間高度耦合, 流程會(huì)比較混亂. 最經(jīng)典就是回調(diào)地獄(callback hell)
.
在實(shí)際開(kāi)發(fā)過(guò)程中, 經(jīng)常能遇到一些業(yè)務(wù)需要先發(fā)送一個(gè)請(qǐng)求獲取數(shù)據(jù)后, 再根據(jù)這個(gè)請(qǐng)求的返回結(jié)果再發(fā)送第二個(gè)請(qǐng)求, 第三個(gè)請(qǐng)求可能又需要第二個(gè)請(qǐng)求的返回結(jié)果作為參數(shù), 如此層層嵌套就稱之為回調(diào)地獄
, 這樣的代碼就很難維護(hù).
ajax('url1', (res1) => {
console.log(res1)
ajax('url2+res1', (res2) => {
console.log(res2)
ajax('url3 + res2', (res) => {
console.log(res3)
...
})
})
})
優(yōu)點(diǎn): 簡(jiǎn)單易懂
缺點(diǎn): 多層回調(diào)函數(shù)嵌套時(shí)容易混亂, 不利于代碼維護(hù)和可讀性, 容易寫出回調(diào)地獄
事件監(jiān)聽(tīng)
這種方式, 異步任務(wù)的執(zhí)行不取決于代碼的執(zhí)行順序, 而取決于某個(gè)事件是否發(fā)生.
element.addEventListener('click', ()=> {
console.log('click')
})
上面這個(gè)例子的含義是, 當(dāng)element
被點(diǎn)擊時(shí), 就會(huì)輸出click
- 優(yōu)點(diǎn): 比較容易理解, 并且可以綁定多個(gè)事件, 每個(gè)事件可以指定多個(gè)回調(diào)函數(shù), 可以比較好的
去耦合
, 有利于實(shí)現(xiàn)模塊化. - 缺點(diǎn): 整個(gè)程序變成了事件驅(qū)動(dòng)型, 運(yùn)行流程很不清晰, 比較難看出主流程, 代碼可讀性低
Promise
Promise
是一種處理異步代碼(并且不會(huì)陷入回調(diào)地獄)的方式
Promise
代表一個(gè)異步操作, 其有三種狀態(tài)pending(進(jìn)行中)
、fulfilled(已完成)
、rejected(已失敗)
, 一個(gè)Promise
對(duì)象必然處于這三種狀態(tài)之一
-
pending
: 初始狀態(tài), 既沒(méi)有完成, 也沒(méi)有被拒絕 -
fulfilled
: 操作成功完成 -
rejected
: 操作失敗
當(dāng)Promise
被調(diào)用后, 它會(huì)以pending
開(kāi)始, 代碼會(huì)繼續(xù)往下執(zhí)行, 而Promise
仍處于pending
狀態(tài), 直到執(zhí)行完成為止, 最終會(huì)以fulfilled
或rejected
結(jié)束, 并對(duì)應(yīng)的傳給回調(diào)函數(shù)–then
或者catch
.
優(yōu)點(diǎn): 不僅可以解決回調(diào)地獄問(wèn)題, 而且能夠捕獲錯(cuò)誤并進(jìn)行處理
缺點(diǎn): 無(wú)法取消Promise
Generator
Generator
函數(shù)是 ES6 提供的一種異步編程解決方案,語(yǔ)法行為與傳統(tǒng)函數(shù)完全不同,Generator
最大的特點(diǎn)就是可以控制函數(shù)的執(zhí)行。
語(yǔ)法上,首先可以把它理解成,Generator
函數(shù)是一個(gè)狀態(tài)機(jī),封裝了多個(gè)內(nèi)部狀態(tài)。Generator
函數(shù)除了狀態(tài)機(jī),還是一個(gè)遍歷器對(duì)象生成函數(shù)。
可暫停函數(shù), yield可暫停,next方法可啟動(dòng),每次返回的是yield后的表達(dá)式結(jié)果。
yield表達(dá)式本身沒(méi)有返回值,或者說(shuō)總是返回undefined。next方法可以帶一個(gè)參數(shù),該參數(shù)就會(huì)被當(dāng)作上一個(gè)yield表達(dá)式的返回值。
async/await
本質(zhì)上async和await是Promise的語(yǔ)法糖, 它可以使得異步代碼看起來(lái)像同步代碼一樣
更加詳細(xì)的Promise可以參考: Promise
總結(jié)
- JS 異步編程進(jìn)化史:
callback
→promise
→generator
→async/await
-
async/await
函數(shù)的實(shí)現(xiàn),就是將Generator
函數(shù)和自動(dòng)執(zhí)行器,包裝在一個(gè)函數(shù)里。 -
async/await
可以說(shuō)是異步終極解決方案了。
(1) async/await
函數(shù)相對(duì)于Promise
, 優(yōu)勢(shì)體現(xiàn)在:
- 處理 then 的調(diào)用鏈,能夠更清晰準(zhǔn)確的寫出代碼
- 并且也能優(yōu)雅地解決回調(diào)地獄問(wèn)題。
當(dāng)然async/await
函數(shù)也存在一些缺點(diǎn): - 因?yàn)?
await
將異步代碼改造成了同步代碼,如果多個(gè)異步代碼沒(méi)有依賴性卻使用了await
會(huì)導(dǎo)致性能上的降低,代碼沒(méi)有依賴性的話,完全可以使用Promise.all
的方式。
(2) async/await
函數(shù)對(duì) Generator 函數(shù)的改進(jìn),體現(xiàn)在以下三點(diǎn):
-
內(nèi)置執(zhí)行器。
Generator 函數(shù)的執(zhí)行必須靠執(zhí)行器,所以才有了 co 函數(shù)庫(kù),而 async 函數(shù)自帶執(zhí)行器。也就是說(shuō),async 函數(shù)的執(zhí)行,與普通函數(shù)一模一樣,只要一行。
-
更廣的適用性。
co 函數(shù)庫(kù)約定,yield 命令后面只能是 Thunk 函數(shù)或 Promise 對(duì)象,而 async 函數(shù)的 await 命令后面,可以跟 Promise 對(duì)象和原始類型的值(數(shù)值、字符串和布爾值,但這時(shí)等同于同步操作)。
-
更好的語(yǔ)義。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-433980.html
async 和 await,比起星號(hào)和 yield,語(yǔ)義更清楚了。async 表示函數(shù)里有異步操作,await 表示緊跟在后面的表達(dá)式需要等待結(jié)果。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-433980.html
到了這里,關(guān)于【前端面經(jīng)】JS-異步解決方案的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!