JS中如何優(yōu)雅的使用async await詳解
jQuery的$.ajax
在開始之前我們先來聊聊我的js異步之路。在我還在學(xué)校的時(shí)候,那時(shí)候還是 jQuery 的天下,我直接接觸到并且經(jīng)常使用的異步操作就是網(wǎng)絡(luò)請(qǐng)求,一手 $.ajax 走天下,伴我過了大二到畢業(yè)后差不多大半年的時(shí)間。
$.ajax( "/xxx" )
.done(function() {
// success !!! do something...
})
.fail(function() {
// fail !!! do something...
})
.always(function() {
// loading finished..
});
不可否認(rèn),$.ajax 這個(gè)東西還是挺好使的,在面對(duì)大部分場(chǎng)景只有一個(gè)請(qǐng)求的情況下,完全勝任甚至覺得很棒
但是有個(gè)大大的問題,那就是面對(duì)請(qǐng)求鏈的時(shí)候就會(huì)特別特別的糟心,比如一個(gè)請(qǐng)求依賴于另一個(gè)請(qǐng)求的結(jié)果,兩個(gè)可能還無所謂,要是五個(gè)八個(gè)的,可能想要直接自殺。。。
$.ajax('/xxx1')
.done(function() {
// success !!! do something...
$.ajax('/xxx2')
.done(function() {
// success !!! do something...
$.ajax('/xxx3')
.done(function() {
// success !!! do something...
$.ajax('/xxx4')
.done(function() {
// success !!! do something...
$.ajax('/xxx5')
.done(function() {
// success !!! do something...
// more...
})
.fail(function() {
// fail !!! do something...
})
.always(function() {
// loading finished..
});
})
.fail(function() {
// fail !!! do something...
})
.always(function() {
// loading finished..
});
})
.fail(function() {
// fail !!! do something...
$.ajax('/xxx6')
.done(function() {
// success !!! do something...
$.ajax('/xxx7')
.done(function() {
// success !!! do something...
// more....
})
.fail(function() {
// fail !!! do something...
})
.always(function() {
// loading finished..
});
})
.fail(function() {
// fail !!! do something...
})
.always(function() {
// loading finished..
});
})
.always(function() {
// loading finished..
});
})
.fail(function() {
// fail !!! do something...
})
.always(function() {
// loading finished..
});
})
.fail(function() {
// fail !!! do something...
})
.always(function() {
// loading finished..
});
抱歉,我不知道你可以套這么多層。。。,但事實(shí)就是TM經(jīng)常出現(xiàn)這樣的流程,大伙兒說說,這不能怪產(chǎn)品吧???只能怪自己學(xué)藝不精
像這樣鏈?zhǔn)讲僮鳎矣X得吧,是個(gè)人可能都是奔潰的,先不說代碼的可讀性,就拿天天在變化的產(chǎn)品需求來說,也許先前是 請(qǐng)求1 結(jié)束之后緊接著 請(qǐng)求2 、 請(qǐng)求3 ,后面產(chǎn)品大手一揮,我覺得這個(gè)流程不大對(duì),后面就變成了 請(qǐng)求2、 請(qǐng)求3 、 請(qǐng)求1,這尼瑪套娃怎么改?可能有人會(huì)有疑問,為啥不用 axios 、 await 、async 呢?這個(gè)就不得不提項(xiàng)目代碼是08年開寫的JSP了。。。。在整了大半年的屎上拉屎以后,迎來了大大的轉(zhuǎn)機(jī),新寫的項(xiàng)目開始往 Vue 上面轉(zhuǎn),并且放棄一部分兼容性,我TM直接起飛。。。
Webpack時(shí)代的開始
新的項(xiàng)目直接Vue + Webpack,我直接就給安排上 axios 、 await 、async ,現(xiàn)在代碼非常好使,嵌套N層的代碼沒了
const r1 = await doSomthing1();
if (r1.xxx === 1) {
const r2 = await doSomthing2(r1);
const r3 = await doSomthing3(r2);
// do something....
} else {
const r4 = await doSomthing4(r1);
const r5 = await doSomthing5(r4);
// do something....
}
// do something....
但是上面的代碼存在一個(gè)問題,如果某個(gè)任務(wù)報(bào)錯(cuò),那么代碼直接就終止了。。。這樣不符合我們的預(yù)期啊,那我們加上 try catch
let r1;
try {
r1 = await doSomthing1();
} catch (e) {
// do something...
return;
}
if (r1) {
if (r1.xxx === 1) {
let r2;
try {
r2 = await doSomthing2(r1);
} catch (e) {
// do something...
return;
}
if (r2) {
let r3;
try {
r3 = await doSomthing3(r2);
} catch (e) {
// do something...
return;
}
// do something...
}
} else {
let r4;
try {
r4 = await doSomthing4(r1);
} catch (e) {
// do something...
return;
}
if (r4) {
let r5;
try {
r5 = await doSomthing5(r4);
} catch (e) {
// do something...
return;
}
}
// do something...
}
// do something...
}
???
優(yōu)化了,等于沒優(yōu)化。。。
這時(shí)候我想聰明的小伙伴可能會(huì)說了,這是啥煎餅玩意兒。而呆滯的小伙伴已經(jīng)開始想怎么解決這樣的問題了。。。
深入了解Promise
我們來看一下 Promise 的定義
/**
* Represents the completion of an asynchronous operation
*/
interface Promise<T> {
/**
* Attaches callbacks for the resolution and/or rejection of the Promise.
* @param onfulfilled The callback to execute when the Promise is resolved.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of which ever callback is executed.
*/
then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): Promise<TResult1 | TResult2>;
/**
* Attaches a callback for only the rejection of the Promise.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of the callback.
*/
catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): Promise<T | TResult>;
}
then 和 catch 都會(huì)返回一個(gè)新的 Promise ,我相信很多小伙伴都已經(jīng)想到了怎么解決方法,需要使用 try catch 是因?yàn)樗鼤?huì)報(bào)錯(cuò),那我們返回一個(gè) 永遠(yuǎn)不會(huì)報(bào)錯(cuò)的結(jié)果 不就行了?說干就干
消滅嵌套
function any(promise) {
return promise.then((v) => v).catch((_) => null);
}
這樣就完全解決了啊???通過判斷是否有值來判斷是否成功,就不用再寫 try catch 了,但是這樣的代碼有點(diǎn)不大好使,如果 then 返回的是一個(gè) void 那么就完?duì)僮恿?,一個(gè) undefined 一個(gè) null ,這還判斷個(gè)錘子,我們?cè)賮砀倪M(jìn)一下
function any(promise) {
return promise
.then((v) => ({ ok: v, hasErr: false }))
.catch((e) => ({ err: e, hasErr: true }));
}
使用的話
const r = await any(doSomething());
if (r.hasErr) {
console.log(r.err);
return;
}
console.log(r.ok);
現(xiàn)在看起來是不是很完美呢,趕緊和小伙伴推銷一下。
小伙伴:???這啥煎餅玩意兒,不用不用。
我:這個(gè)我寫的,在異步中用起來很好使的,告別嵌套 try catch ,巴拉巴拉。。。
小伙伴:好的,下次一定用。
大家肯定有遇到過這樣的情況,大家寫的代碼互相看不起,只要不是三方庫(kù),大家都是能不用同事寫的就不用。。。
await-to-js
我都以為只有我一人欣賞,這一份優(yōu)雅。事情出現(xiàn)轉(zhuǎn)機(jī),某天我正在刷github,發(fā)現(xiàn)了一個(gè)和我差不多異曲同工之妙的東西 await-to-js ,幾行代碼透露了和我一樣的執(zhí)著
// 下面是最新的代碼
/**
* @param { Promise } promise
* @param { Object= } errorExt - Additional Information you can pass to the err object
* @return { Promise }
*/
export function to<T, U = Error> (
promise: Promise<T>,
errorExt?: object
): Promise<[U, undefined] | [null, T]> {
return promise
.then<[null, T]>((data: T) => [null, data])
.catch<[U, undefined]>((err: U) => {
if (errorExt) {
Object.assign(err, errorExt);
}
return [err, undefined];
});
}
export default to;
再貼上使用示例
import to from 'await-to-js';
// If you use CommonJS (i.e NodeJS environment), it should be:
// const to = require('await-to-js').default;
async function asyncTaskWithCb(cb) {
let err, user, savedTask, notification;
[ err, user ] = await to(UserModel.findById(1));
if(!user) return cb('No user found');
[ err, savedTask ] = await to(TaskModel({userId: user.id, name: 'Demo Task'}));
if(err) return cb('Error occurred while saving task');
if(user.notificationsEnabled) {
[ err ] = await to(NotificationService.sendNotification(user.id, 'Task Created'));
if(err) return cb('Error while sending notification');
}
if(savedTask.assignedUser.id !== user.id) {
[ err, notification ] = await to(NotificationService.sendNotification(savedTask.assignedUser.id, 'Task was created for you'));
if(err) return cb('Error while sending notification');
}
cb(null, savedTask);
}
async function asyncFunctionWithThrow() {
const [err, user] = await to(UserModel.findById(1));
if (!user) throw new Error('User not found');
}
是不是感覺回來了,嵌套不再。。。
為了讓小伙伴用上一行的代碼,我只能忍痛推薦 await-to-js ,發(fā)上github地址,小伙伴:八百多star (ps: 現(xiàn)在2K+) 質(zhì)量可靠,看了一下示例,嗯嗯,很不錯(cuò),很完美,后面。。。后面的事不用我多說了,我自己寫的也全換成了 await-to-js 。。。
我待世界如初戀,初戀卻傷我千百遍
總結(jié)
我實(shí)現(xiàn)的版本其實(shí)存在著一點(diǎn)點(diǎn)問題的,在JS這樣 靈活 的語言中,我改了返回值,別人就能直接抄我的家,類型不夠嚴(yán)謹(jǐn),要是放TS里,那就只能說一點(diǎn)小毛病,新加了 ok 、 err 、 hasErr 增加了一小點(diǎn)點(diǎn)case,但并不致命
await-to-js 中一點(diǎn)點(diǎn)的設(shè)計(jì)哲學(xué),為啥把錯(cuò)誤放在數(shù)組的第一個(gè)位置,而不是把成功放在第一個(gè)位置,就很明示:永遠(yuǎn)謹(jǐn)記錯(cuò)誤,把錯(cuò)誤放在第一位,而不是很 自信 成功,就忘記錯(cuò)誤的慘痛。
const [, result] = await to(iWillSucceed());
參考資料
到此這篇關(guān)于JS中如何優(yōu)雅的使用async await的文章就介紹到這了,更多相關(guān)JS優(yōu)雅使用async await內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
微信小程序模板消息限制實(shí)現(xiàn)無限制主動(dòng)推送的示例代碼
這篇文章主要介紹了微信小程序模板消息限制實(shí)現(xiàn)無限制主動(dòng)推送的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08
Webpack實(shí)現(xiàn)按需打包Lodash的幾種方法詳解
這篇文章主要給大家介紹了關(guān)于Webpack實(shí)現(xiàn)按需打包Lodash的幾種方法,文中介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧。2017-05-05
axios實(shí)現(xiàn)取消請(qǐng)求的方法詳解
axios 是現(xiàn)在前端項(xiàng)目中最常使用的一個(gè)請(qǐng)求庫(kù),目前 Github star 已經(jīng)達(dá)到了 104k star,本文我們討論的問題是axios 是如何實(shí)現(xiàn)取消請(qǐng)求(Cancel requests)的,文中有詳細(xì)的實(shí)現(xiàn)方法,感興趣的朋友可以參考下2024-04-04
webpack5的entry和output配置小白學(xué)習(xí)
這篇文章主要為大家介紹了webpack5的entry和output使用配置小白學(xué)習(xí)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05
基于javascript實(shí)現(xiàn)按圓形排列DIV元素(二)
本篇文章主要介紹基于javascript實(shí)現(xiàn)按圓形排列DIV元素的方法,此文著重于介紹怎樣實(shí)現(xiàn)的按圓形排列DIV元素的運(yùn)動(dòng)原理和實(shí)現(xiàn)效果代碼,需要的朋友來看下吧2016-12-12
JavaScript實(shí)現(xiàn)顯示隱藏表單文字
這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)顯示隱藏表單文字,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09
js中json對(duì)象和字符串的理解及相互轉(zhuǎn)化操作實(shí)現(xiàn)方法
這篇文章主要介紹了js中json對(duì)象和字符串的理解及相互轉(zhuǎn)化操作實(shí)現(xiàn)方法,結(jié)合實(shí)例形式分析了json對(duì)象與字符串的功能以及相互轉(zhuǎn)換操作實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-09-09

