JS中如何优雅的使用async await详解

作者:尽管如此世界依然美丽 时间:2024-05-02 16:19:23 

目录
  • jQuery的$.ajax

  • Webpack时代的开始

  • 深入了解Promise

  • 消灭嵌套

  • await-to-js

  • 总结

jQuery的$.ajax

在开始之前我们先来聊聊我的js异步之路。在我还在学校的时候,那时候还是 jQuery 的天下,我直接接触到并且经常使用的异步操作就是网络请求,一手 $.ajax 走天下,伴我过了大二到毕业后差不多大半年的时间。


$.ajax( "/xxx" )
 .done(function() {
   // success !!! do something...
 })
 .fail(function() {
   // fail !!! do something...
 })
 .always(function() {
   // loading finished..
 });

不可否认,$.ajax 这个东西还是挺好使的,在面对大部分场景只有一个请求的情况下,完全胜任甚至觉得很棒

但是有个大大的问题,那就是面对请求链的时候就会特别特别的糟心,比如一个请求依赖于另一个请求的结果,两个可能还无所谓,要是五个八个的,可能想要直接 * 。。。


$.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..
 });

抱歉,我不知道你可以套这么多层。。。,但事实就是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);
 // do something....
} else {
 const r4 = await doSomthing4(r1);
 const r5 = await doSomthing5(r4);
 // do something....
}
// do something....

但是上面的代码存在一个问题,如果某个任务报错,那么代码直接就终止了。。。这样不符合我们的预期啊,那我们加上 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...
}

???

优化了,等于没优化。。。

这时候我想聪明的小伙伴可能会说了,这是啥煎饼玩意儿。而呆滞的小伙伴已经开始想怎么解决这样的问题了。。。

深入了解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 都会返回一个新的 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 }));
}

使用的话


const r = await any(doSomething());
if (r.hasErr) {
 console.log(r.err);
 return;
}
console.log(r.ok);

现在看起来是不是很完美呢,赶紧和小伙伴推销一下。

小伙伴:???这啥煎饼玩意儿,不用不用。

我:这个我写的,在异步中用起来很好使的,告别嵌套  try catch ,巴拉巴拉。。。

小伙伴:好的,下次一定用。

大家肯定有遇到过这样的情况,大家写的代码互相看不起,只要不是三方库,大家都是能不用同事写的就不用。。。

await-to-js

我都以为只有我一人欣赏,这一份优雅。事情出现转机,某天我正在刷github,发现了一个和我差不多异曲同工之妙的东西 await-to-js ,几行代码透露了和我一样的执着


// 下面是最新的代码
/**
* @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 ,发上github地址,小伙伴:八百多star (ps: 现在2K+) 质量可靠,看了一下示例,嗯嗯,很不错,很完美,后面。。。后面的事不用我多说了,我自己写的也全换成了 await-to-js 。。。

我待世界如初恋,初恋却伤我千百遍

  • $.ajax

  • Promise

  • await-to-js

来源:https://juejin.cn/post/7014749066005331975

标签:js,async,await
0
投稿

猜你喜欢

  • pandas条件组合筛选和按范围筛选的示例代码

    2023-06-13 07:15:37
  • python的keyword模块用法实例分析

    2022-04-06 07:56:22
  • Python实现鼠标自动在屏幕上随机移动功能

    2022-01-30 05:08:15
  • JS实现合并json对象的方法

    2023-08-29 15:09:30
  • 使用单通道实现半透明效果

    2009-12-12 17:40:00
  • 解决python对齐错误的方法

    2023-08-11 05:40:07
  • 解析Anaconda创建python虚拟环境的问题

    2023-08-01 03:58:13
  • JavaScript学习心得之如何走出初学困境

    2008-12-24 13:30:00
  • python在协程中增加任务实例操作

    2023-02-17 22:57:48
  • Python实现socket库网络通信套接字

    2023-08-07 17:38:59
  • Python 用三行代码提取PDF表格数据

    2023-10-08 18:54:57
  •  分享Python 中的 7 种交叉验证方法

    2023-09-18 19:10:33
  • Vue 通过自定义指令回顾v-内置指令(小结)

    2023-07-02 16:56:42
  • Active Server Pages 错误 “ASP 0141”

    2009-08-19 17:10:00
  • Python 异步如何使用等待有时间限制协程

    2022-02-12 09:29:28
  • centos下安装配置phpMyAdmin的方法步骤

    2023-11-09 01:27:11
  • 浏览器是怎样工作的二:渲染引擎 HTML解析

    2012-05-09 20:34:20
  • python高并发异步服务器核心库forkcore使用方法

    2021-12-04 18:38:24
  • css网页下拉菜单制作方法(2):初步实现

    2007-02-03 11:39:00
  • golang设置http response响应头与填坑记录

    2024-05-21 10:22:24
  • asp之家 网络编程 m.aspxhome.com