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


Posted in Javascript onOctober 05, 2021

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 。。。

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

总结

我实现的版本其实存在着一点点问题的,在JS这样 灵活 的语言中,我改了返回值,别人就能直接抄我的家,类型不够严谨,要是放TS里,那就只能说一点小毛病,新加了 ok 、 err 、 hasErr 增加了一小点点case,但并不致命

await-to-js 中一点点的设计哲学,为啥把错误放在数组的第一个位置,而不是把成功放在第一个位置,就很明示:永远谨记错误,把错误放在第一位,而不是很 自信 成功,就忘记错误的惨痛。

const [, result] = await to(iWillSucceed());

参考资料

到此这篇关于JS中如何优雅的使用async await的文章就介绍到这了,更多相关JS优雅使用async await内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
jquery.cookie.js 操作cookie实现记住密码功能的实现代码
Apr 27 Javascript
Three.js源码阅读笔记(光照部分)
Dec 27 Javascript
iframe异步加载实现点击左边菜单加载右边内容实例讲解
Mar 04 Javascript
Jquery动态进行图片缩略的原理及实现
Aug 13 Javascript
XMLHttpRequest处理xml格式的返回数据(示例代码)
Nov 21 Javascript
两种不同的方法实现js对checkbox进行全选和反选
May 13 Javascript
浅谈vux之x-input使用以及源码解读
Nov 04 Javascript
判断iOS、Android以及PC端的示例代码
Nov 15 Javascript
JavaScript数组特性与实践应用深入详解
Dec 30 Javascript
javascript 对象 与 prototype 原型用法实例分析
Nov 11 Javascript
Node.js API详解之 console模块用法详解
May 12 Javascript
JavaScript实现轮播图效果
Oct 30 Javascript
js中Object.create实例用法详解
Oct 05 #Javascript
TypeScript中条件类型精读与实践记录
Oct 05 #Javascript
SSM VUE Axios详解
Ajax实现三级联动效果
Oct 05 #Javascript
5种 JavaScript 方式实现数组扁平化
Oct 05 #Javascript
国庆节到了,利用JS实现一个生成国庆风头像的小工具 详解实现过程
Oct 05 #Javascript
Javascript设计模式之原型模式详细
You might like
欧美媒体选出10年前最流行的17部动画
2017/01/18 日漫
FastCGI 进程意外退出造成500错误
2015/07/26 PHP
使用正则去除php代码中的注释方法
2016/11/03 PHP
详解php实现页面静态化原理
2017/06/21 PHP
php处理静态页面:页面设置缓存时间实例
2017/06/22 PHP
php使用socket调用http和smtp协议实例小结
2019/07/26 PHP
用jquery来定位
2007/02/20 Javascript
通过伪协议解决父页面与iframe页面通信的问题
2015/04/05 Javascript
开源免费天气预报接口API及全国所有地区代码(国家气象局提供)
2016/12/26 Javascript
详解webpack+angular2开发环境搭建
2017/06/28 Javascript
用js屏蔽被http劫持的浮动广告实现方法
2017/08/10 Javascript
浅谈react.js中实现tab吸顶效果的问题
2017/09/06 Javascript
vue 微信授权登录解决方案
2018/04/10 Javascript
详解在vue-cli项目下简单使用mockjs模拟数据
2018/10/19 Javascript
JavaScript使用闭包模仿块级作用域操作示例
2019/01/21 Javascript
JavaScript如何实现元素全排列实例代码
2019/05/14 Javascript
JS实现简易留言板特效
2019/12/23 Javascript
24个ES6方法解决JS实际开发问题(小结)
2020/05/31 Javascript
Jquery如何使用animation动画效果改变背景色的代码
2020/07/20 jQuery
jquery实现淡入淡出轮播图效果
2020/12/13 jQuery
python用户管理系统的实例讲解
2017/12/23 Python
python正向最大匹配分词和逆向最大匹配分词的实例
2018/11/14 Python
python通过TimedRotatingFileHandler按时间切割日志
2019/07/17 Python
Python如何调用JS文件中的函数
2019/08/16 Python
python统计函数库scipy.stats的用法解析
2020/02/25 Python
python 如何调用 dubbo 接口
2020/09/24 Python
pandas按条件筛选数据的实现
2021/02/20 Python
css3实现input输入框颜色渐变发光效果代码
2014/04/02 HTML / CSS
size?荷兰官方网站:英国高级运动鞋精品店
2020/07/24 全球购物
口腔工艺技术专业毕业生自荐信
2013/09/27 职场文书
上课玩手机检讨书
2014/02/08 职场文书
党组织公开承诺书
2014/03/29 职场文书
领导干部整治奢华浪费之风思想汇报
2014/10/07 职场文书
2014财务部年度工作总结
2014/12/08 职场文书
单独二胎证明
2015/06/24 职场文书
会计做账心得体会
2016/01/22 职场文书