JS中如何优雅的使⽤asyncawait详解
⽬录
jQuery的$.ajax
Webpack时代的开始
深⼊了解Promise
消灭嵌套
await-to-js
总结
jQuery的$.ajax
在开始之前我们先来聊聊我的js异步之路。在我还在学校的时候,那时候还是 jQuery 的天下,我直接接触到并且经常使⽤的异步操作就是⽹络请求,⼀⼿⾛天下,伴我过了⼤⼆到毕业后差不多⼤半年的时间。
$.ajax( "/xxx" )
.done(function() {
// success
})
.fail(function() {
// fail
})
.always(function() {
// loading finished..
});
不可否认,$.ajax 这个东西还是挺好使的,在⾯对⼤部分场景只有⼀个请求的情况下,完全胜任甚⾄觉得很棒
但是有个⼤⼤的问题,那就是⾯对请求链的时候就会特别特别的糟⼼,⽐如⼀个请求依赖于另⼀个请求的结果,两个可能还⽆所谓,要是五个⼋个的,可能想要直接⾃杀。。。
$.ajax('/xxx1')
.done(function() {
// success
$.ajax('/xxx2')
.done(function() {
// success
$.ajax('/xxx3')
.done(function() {
// success
$.ajax('/xxx4')
.done(function() {
// success
$.ajax('/xxx5')
.done(function() {
// success
//
})
.fail(function() {
// fail
})
.always(function() {
/
/ loading finished..
});
})
.fail(function() {
// fail
})
.always(function() {
// loading finished..
});
})
.fail(function() {
/
/ fail
$.ajax('/xxx6')
.done(function() {
// success
$.ajax('/xxx7')
.done(function() {
// success
//
})
.fail(function() {
// fail
})
.always(function() {
// loading finished..
});
})
.fail(function() {
// fail
})
.always(function() {
// loading finished..
});
})
.always(function() {
// loading finished..
});
})
.fail(function() {
// fail
})
.always(function() {
// loading finished..
});
})
.fail(function() {
// fail
})
.always(function() {
// loading finished..
});
抱歉,我不知道你可以套这么多层。。。,但事实就是TM经常出现这样的流程,⼤伙⼉说说,这不能怪产品吧只能怪⾃⼰学艺不精
像这样链式操作,我觉得吧,是个⼈可能都是奔溃的,先不说代码的可读性,就拿天天在变化的产品需求来说,也许先前是请求1 结束之后紧接着请求2 、请求3 ,后⾯产品⼤⼿⼀挥,我觉得这个流程不⼤对,后⾯就变成了请求2、请求3 、请求1,这套娃怎么改?可能有⼈会有疑问,为啥不⽤ axios 、
await 、async 呢?这个就不得不提项⽬代码是08年开写的JSP了。。。。在整了⼤半年的屎上拉屎以后,迎来了⼤⼤的转机,新写的项⽬开始往 Vue 上⾯转,并且放弃⼀部分兼容性,我TM直接起飞。。。
Webpack时代的开始
新的项⽬直接Vue + Webpack,我直接就给安排上 axios 、 await 、async ,现在代码⾮常好使,嵌套N层的代码没了
const r1 = await doSomthing1();
if (r1.xxx === 1) {
const r2 = await doSomthing2(r1);
const r3 = await doSomthing3(r2);
//
} else {
const r4 = await doSomthing4(r1);
const r5 = await doSomthing5(r4);
//
}
//
但是上⾯的代码存在⼀个问题,如果某个任务报错,那么代码直接就终⽌了。。。这样不符合我们的预期啊,那我们加上 try catch
let r1;
try {
r1 = await doSomthing1();
} catch (e) {
//
return;
}
if (r1) {
if (r1.xxx === 1) {
let r2;
try {
r2 = await doSomthing2(r1);
} catch (e) {
//
return;
}
if (r2) {
let r3;
try {
r3 = await doSomthing3(r2);
} catch (e) {
//
return;
}
//
}
} else {
let r4;
try {
r4 = await doSomthing4(r1);
} catch (e) {
//
return;
}
if (r4) {
let r5;
try {
r5 = await doSomthing5(r4);
} catch (e) {
//
return;
}
}
//
}
//
}
优化了,等于没优化。。。
这时候我想聪明的⼩伙伴可能会说了,这是啥煎饼玩意⼉。⽽呆滞的⼩伙伴已经开始想怎么解决这样的问题了。。。
深⼊了解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 都会返回⼀个新的 Promise ,我相信很多⼩伙伴都已经想到了怎么解决⽅法,需要使⽤ try catch 是因为它会报错,那我们返回⼀个永远不会报错的结果不就⾏了?说⼲就⼲
消灭嵌套
function any(promise) {
return promise.then((v) => v).catch((_) => null);
}
这样就完全解决了啊通过判断是否有值来判断是否成功,就不⽤再写 try catch 了,但是这样的代码有点不⼤好使,如果 then 返回的是⼀个 void 那么就完犊⼦了,⼀个 undefined ⼀个 null ,这还判断个锤⼦,我们再来改进⼀下
function any(promise) {
return promise
.then((v) => ({ ok: v, hasErr: false }))
.catch((e) => ({ err: e, hasErr: true }));
js assign}
使⽤的话
const r = await any(doSomething());
if (r.hasErr) {
console.);
return;
}
console.log(r.ok);
现在看起来是不是很完美呢,赶紧和⼩伙伴推销⼀下。
⼩伙伴:这啥煎饼玩意⼉,不⽤不⽤。
我:这个我写的,在异步中⽤起来很好使的,告别嵌套 try catch ,巴拉巴拉。。。
⼩伙伴:好的,下次⼀定⽤。
⼤家肯定有遇到过这样的情况,⼤家写的代码互相看不起,只要不是三⽅库,⼤家都是能不⽤同事写的就不⽤。。。
await-to-js
我都以为只有我⼀⼈欣赏,这⼀份优雅。事情出现转机,某天我正在刷github,发现了⼀个和我差不多异曲同⼯之妙的东西,⼏⾏代码透露了和我⼀样的执着
// 下⾯是最新的代码
/**
* @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');
ificationsEnabled) {
[ 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 ,发上github地址,⼩伙伴:⼋百多star (ps: 现在2K+) 质量可靠,看了⼀下⽰例,嗯嗯,很不错,很完美,后⾯。。。后⾯的事不⽤我多说了,我⾃⼰写的也全换成了。。。
我待世界如初恋,初恋却伤我千百遍
总结
我实现的版本其实存在着⼀点点问题的,在JS这样灵活的语⾔中,我改了返回值,别⼈就能直接抄我的家,类型不够严谨,要是放TS⾥,那就只能说⼀点⼩⽑病,新加了 ok 、 err 、 hasErr 增加了⼀⼩点点case,但并不致命
中⼀点点的设计哲学,为啥把错误放在数组的第⼀个位置,⽽不是把成功放在第⼀个位置,就很明⽰:永远谨记错误,把错误放在第⼀位,⽽不是很⾃信成功,就忘记错误的惨痛。
const [, result] = await to(iWillSucceed());
参考资料
$.ajax
Promise
await-to-js
到此这篇关于JS中如何优雅的使⽤async await的⽂章就介绍到这了,更多相关JS优雅使⽤async await内容请搜索以前的⽂章或继续浏览下⾯的相关⽂章希望⼤家以后多多⽀持!
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论