JavaScript體驗(yàn)異步更好的解決辦法
一、異步解決方案的進(jìn)化史
JavaScript的異步操作一直是個(gè)麻煩事,所以不斷有人提出它的各種解決方案??梢宰匪莸阶钤绲幕卣{(diào)函數(shù)(ajax老朋友),到Promise(不算新的朋友),再到ES6的Generator(強(qiáng)勁的朋友)。
幾年前我們可能用過(guò)一個(gè)比較著名的Async.js,但是它沒(méi)有擺脫回調(diào)函數(shù),并且錯(cuò)誤處理也是按照“回調(diào)函數(shù)的第一個(gè)參數(shù)用來(lái)傳遞錯(cuò)誤”這樣一個(gè)約定。而眾所周知的回調(diào)地獄仍然是一個(gè)比較突出的問(wèn)題,直到Generator改變了這種異步風(fēng)格。
但是ES7的async await的出現(xiàn)(碉堡的新朋友),我們可以輕松寫(xiě)出同步風(fēng)格的代碼同時(shí)又擁有異步機(jī)制,可以說(shuō)是目前最簡(jiǎn)單,最優(yōu)雅,最佳的解決方案了。
二、async await語(yǔ)法
async await語(yǔ)法比較簡(jiǎn)單,可以認(rèn)為是Generator的語(yǔ)法糖,比起星號(hào)和yield更具有語(yǔ)義化。下面一個(gè)簡(jiǎn)單的例子表示1秒之后輸出hello world:
function timeout(ms) { return new Promise((resolve) => { setTimeout(resolve, ms); }); } async function asyncPrint(value, ms) { await timeout(ms); console.log(value) } asyncPrint('hello world', 1000);
await只能用在async函數(shù)中,如果用在普通函數(shù)就會(huì)報(bào)錯(cuò)
await后面跟的是一個(gè)Promise對(duì)象(當(dāng)然其它值也可以,但是會(huì)包裝成一個(gè)立即resolve的Promise,也就沒(méi)有意義了)
await會(huì)等待Promise的結(jié)果返回再繼續(xù)執(zhí)行
await等待的雖然是Promise對(duì)象,但是不必寫(xiě).then(),直接可以得到返回值,將上面的代碼微調(diào),發(fā)現(xiàn)返回值result也是可以輸出hello world:
function timeout(ms) { return new Promise((resolve) => { setTimeout(_ => {resolve('hello world')}, ms); }); } async function asyncPrint(ms) { let result = await timeout(ms); console.log(result) } asyncPrint(1000);
三、async await錯(cuò)誤處理
前面說(shuō)了await等待的雖然是Promise對(duì)象,但是不必寫(xiě).then(),所以其實(shí)也不用寫(xiě).catch()了,直接用try catch就能捕捉錯(cuò)誤,這樣可以避免錯(cuò)誤處理代碼非常冗余和笨重,還是將上面的例子微調(diào):
function timeout(ms) { return new Promise((resolve, reject) => { setTimeout(_ => {reject('error')}, ms);//reject模擬出錯(cuò),返回error }); } async function asyncPrint(ms) { try { console.log('start'); await timeout(ms);//這里返回了錯(cuò)誤 console.log('end');//所以這句代碼不會(huì)被執(zhí)行了 } catch(err) { console.log(err); //這里捕捉到錯(cuò)誤error } } asyncPrint(1000);
如果有多個(gè)await,可以一起放在try catch中:
async function main() { try { const async1 = await firstAsync(); const async2 = await secondAsync(); const async3 = await thirdAsync(); } catch (err) { console.error(err); } }
四、async await注意點(diǎn)
1). 前面已經(jīng)說(shuō)過(guò),await命令后面的Promise對(duì)象,運(yùn)行結(jié)果很可能是reject或邏輯報(bào)錯(cuò),所以最好把a(bǔ)wait放在try catch代碼塊中。
2). 多個(gè)await命令的異步操作,如果不存在依賴關(guān)系,讓它們同時(shí)觸發(fā)。
const async1 = await firstAsync(); const async2 = await secondAsync();
上面代碼中,async1和async2如果是兩個(gè)獨(dú)立的異步操作,這樣寫(xiě)會(huì)比較耗時(shí),因?yàn)橹挥衒irstAsync完成以后,才會(huì)執(zhí)行secondAsync,完全可以用Promise.all優(yōu)雅地處理:
let [async1, async2] = await Promise.all([firstAsync(), secondAsync()]);
3). await只能用在async函數(shù)之中,如果用在普通函數(shù)就會(huì)報(bào)錯(cuò):
async function main() { let docs = [{}, {}, {}]; //報(bào)錯(cuò) await is only valid in async function docs.forEach(function (doc) { await post(doc); console.log('main'); }); } function post(){ return new Promise((resolve) => { setTimeout(resolve, 1000); }); }
在forEach內(nèi)部方法加上async就可以了:
async function main() { let docs = [{}, {}, {}]; docs.forEach(async function (doc) { await post(doc); console.log('main'); }); } function post(){ return new Promise((resolve) => { setTimeout(resolve, 1000); }); }
但是你會(huì)發(fā)現(xiàn)3個(gè)main是同時(shí)輸出的,這就說(shuō)明post是并發(fā)執(zhí)行的,而不是繼發(fā)執(zhí)行,改成for就可以解決問(wèn)題,3個(gè)main是分別相隔1秒輸出:
async function main() { let docs = [{}, {}, {}]; for (let doc of docs) { await post(doc); console.log('main'); } } function post(){ return new Promise((resolve) => { setTimeout(resolve, 1000); }); }
總之,用了async await之后整個(gè)人神清氣爽,可以用非常簡(jiǎn)潔和優(yōu)雅的代碼實(shí)現(xiàn)各種花式異步操作,并且在業(yè)務(wù)邏輯復(fù)雜的情況下可以不用陷入回調(diào)地獄中。不敢說(shuō)這一定是終極的解決方案,但確實(shí)是目前最優(yōu)雅的解決方案!
- vue.js 初體驗(yàn)之Chrome 插件開(kāi)發(fā)實(shí)錄
- 幾個(gè)優(yōu)化WordPress中JavaScript加載體驗(yàn)的插件介紹
- nodejs初步體驗(yàn)篇
- JavaScript實(shí)現(xiàn)統(tǒng)計(jì)文本框Textarea字?jǐn)?shù)增強(qiáng)用戶體驗(yàn)
- Javascript類庫(kù)的頂層對(duì)象名用戶體驗(yàn)分析
- JS+CSS實(shí)現(xiàn)的一種交互體驗(yàn) 表單頁(yè)面
- js驗(yàn)證符合用戶體驗(yàn)的網(wǎng)頁(yè)表單特效
- 用javascript實(shí)現(xiàn)改善用戶體驗(yàn)之a(chǎn)lert提示效果
- JS如何讓你的移動(dòng)端交互體驗(yàn)更加優(yōu)秀
相關(guān)文章
自用js開(kāi)發(fā)框架小成 學(xué)習(xí)js的朋友可以看看
前段時(shí)間項(xiàng)目需要用到j(luò)s樹(shù),找了好多都不符合項(xiàng)目需求,后來(lái)發(fā)現(xiàn)了梅花雪樹(shù)和js框架,類似C#名稱空間的用法讓我眼前一亮,遂拿來(lái)主義,讀了幾遍代碼后就開(kāi)工了(我是個(gè)急性子呵呵),完成了大部分,最近才找出來(lái)測(cè)試了下。2010-11-11深入理解JavaScript的事件執(zhí)行機(jī)制
本文主要介紹了JavaScript的事件執(zhí)行機(jī)制,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09JS設(shè)計(jì)模式之狀態(tài)模式的用法使用方法
JavaScript狀態(tài)模式是一種行為型設(shè)計(jì)模式,核心是對(duì)象在其內(nèi)部狀態(tài)改變時(shí)改變其行為,狀態(tài)模式將對(duì)象的行為封裝到不同的狀態(tài)類中,使得對(duì)象在不同狀態(tài)下可以選擇不同的行為,本文給大家詳細(xì)的介紹一下?tīng)顟B(tài)設(shè)計(jì)模式在Js中的使用,需要的朋友可以參考下2023-08-08Vue.js實(shí)現(xiàn)頁(yè)面后退時(shí)還原滾動(dòng)位置的操作方法
Vuet看起來(lái)也不是很復(fù)雜,只需要定義好模塊狀態(tài),然后在組件中設(shè)置對(duì)應(yīng)的規(guī)則來(lái)更新模塊的狀態(tài)即可,這篇文章主要介紹了Vue.js實(shí)現(xiàn)頁(yè)面后退時(shí)還原滾動(dòng)位置的實(shí)現(xiàn)方法,需要的朋友可以參考下2022-07-07JavaScript運(yùn)動(dòng)框架 解決防抖動(dòng)問(wèn)題、懸浮對(duì)聯(lián)(二)
這篇文章主要為大家詳細(xì)介紹了JavaScript運(yùn)動(dòng)框架的第二部分,解決防抖動(dòng)問(wèn)題、懸浮對(duì)聯(lián)問(wèn)題,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05利用element-ui實(shí)現(xiàn)遠(yuǎn)程搜索兩種實(shí)現(xiàn)方式
這篇文章主要介紹了利用element-ui的兩種遠(yuǎn)程搜索實(shí)現(xiàn)代碼,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2023-12-12學(xué)習(xí)JS中的DOM節(jié)點(diǎn)以及操作
本篇文章給大家整理了關(guān)于JS中DOM節(jié)點(diǎn)的相關(guān)知識(shí)點(diǎn)以及代碼實(shí)例,有興趣的朋友可以跟著學(xué)習(xí)下。2018-04-04