• 【JS】await异常捕获,这样做才完美


    🍊缘由

    JS中async/await异步调用,只能通过try-catch吗?

    🍍你想听的故事:

    作为一个合格的全栈搬砖工,那必须文武双全,前后必备。遂吾日三省吾身,偶发觉前端长时间不写有些落下,便抽用了摸鱼的时间,检查一下前端小老弟的代码。

    不查不知道,一查吓一跳。乍一看小老弟代码清新脱俗,特别规整,但存重大的隐患:


    不知道大家看没看出,里面竟然没有异常捕获,完全逻辑代码一把嗦。可能有小伙伴说,我平时也这么写,也没遇到什么大坑,大不了页面报错呗,改就完了。

    话虽如此,有坑没趟上,不代表永远顺风输水,如果不进行异常捕获,那么页面逻辑可能会因为此处异常戛然而止,导致后续业务无法正常执行

    光描述可能小伙伴还是没有代码上概念,下面举一个例子便一目了然

    举个栗子🌰

    有个页面,需要展示会员信息和推荐商品,前端页面需要调用后端接口并把两者赋值,显示到页面上

    正常情况:调异步接口正常返回

    此处模拟前端调用后端两个接口,分别获取会员信息商品信息两者互不影响,并打印。
    小伙伴们可以看到,和我们预想一样,没有趟坑,非常顺利,没有任何问题

    // 实例模拟页面初始化调用两个异步接口,并打印成功返回的数据
    const getMember = async () => {
        return new Promise((resolve, reject) => {
            // 模拟接口获取会员数据
            setTimeout(() => {
                // 成功返回
                resolve(
                    {
                        name: '张三',
                        age: 18
                    }
                )
            }, 1000)
        })
    }
    
    const getGoods = async () => {
        return new Promise((resolve, reject) => {
            // 模拟接口获取商品数据
            setTimeout(() => {
                // 成功返回
                resolve(
                    [{
                        name: '面包',
                        price: 5
                    },{
                        name: '牛奶',
                        price: 6
                    }]
                )
            }, 2000)
        })
    }
    
    const init = async () => {
        const member = await getMember();
        console.log("会员:", member)
    
        const goods = await getGoods();
        console.log("商品:", JSON.stringify(goods))
    }
    
    init();
    
    // 打印结果
    // 会员: { name: '张三', age: 18 }
    // 商品: [{"name":"面包","price":5},{"name":"牛奶","price":6}]
    


    异常情况:调异步接口且未捕获异常

    此处同上模拟前端调用后端两个接口,此时会员信息接口出现问题异常,商品信息接口正常
    小伙伴们可以看到,此时就掉入没有捕获异常的的坑了,非常遗憾,后续牵连接口一并因错误戛然而止,出错连坐!

    // 实例模拟页面初始化调用两个异步接口,第一个获取会员信息异常,第二个商品数据正常
    const getMember = async () => {
        return new Promise((resolve, reject) => {
            // 模拟获取会员数据接口异常
            setTimeout(() => {
                // 成功返回
                reject(new Error('会员接口异常'))
            }, 1000)
        })
    }
    
    const getGoods = async () => {
        return new Promise((resolve, reject) => {
            // 模拟接口获取商品数据
            setTimeout(() => {
                // 成功返回
                resolve(
                    [{
                        name: '面包',
                        price: 5
                    },{
                        name: '牛奶',
                        price: 6
                    }]
                )
            }, 2000)
        })
    }
    
    const init = async () => {
        const member = await getMember();
        console.log("会员:", member)
    
        const goods = await getGoods();
        console.log("商品:", JSON.stringify(goods))
    }
    
    init();
    
    // 打印结果
    // 直接报异常,并且后面正常的商品接口console.log都没有打印内容
    
    

    这种情况就导致,后续无关接口也无法正常返回,从而页面可能直接崩溃

    综上案例,所以异常必须捕获,而且要捕获的漂亮


    🎯主要目标

    实现重点

    async/await异常捕获方法

    正文

    🥦目标解析

    async/await异常捕获方法

    方法一:try-catch

    任何异步调用全部一把嗦,全部套上try-catch壳

    还是以上述内容为案例,将调用接口加入try-catch后,第一个接口异常后,后续接口不受影响

    try{
            // 加入try-catch异常捕获
            const member = await getMember();
            console.log("会员:", member)
        }catch (e){
            console.error("会员接口异常:", e)
        }
    
    // 完整代码
    const getMember = async () => {
        return new Promise((resolve, reject) => {
            // 模拟获取会员数据接口异常
            setTimeout(() => {
                // 成功返回
                reject(new Error('会员接口异常'))
            }, 1000)
        })
    }
    
    const getGoods = async () => {
        return new Promise((resolve, reject) => {
            // 模拟接口获取商品数据
            setTimeout(() => {
                // 成功返回
                resolve(
                    [{
                        name: '面包',
                        price: 5
                    },{
                        name: '牛奶',
                        price: 6
                    }]
                )
            }, 2000)
        })
    }
    
    const init = async () => {
        try{
            // 加入try-catch异常捕获
            const member = await getMember();
            console.log("会员:", member)
        }catch (e){
            console.error("会员接口异常:", e)
        }
    
        try{
            // 加入try-catch异常捕获
            const goods = await getGoods();
            console.log("商品:", JSON.stringify(goods))
        }catch (e){
            console.error("商品异常:", e)
        }
    }
    
    init();
    
    // 打印结果
    // 会员接口异常:xxxxx
    // 商品: [{"name":"面包","price":5},{"name":"牛奶","price":6}]
    
    

    try-catch的弊端

    接口少还好说,无脑加;接口多就会出现大批的try-catch军团,代码冗余并且十分混乱,阅读十分吃力!


    方法二:使用 Promise 处理

    解释一下:await 命令后面是一个 Promise 对象,直接可以使用.catch来捕获异常

    // 直接后面跟着.catch
    const member = await getMember().catch((err) => {console.error("会员接口异常:", err)});
    
    // 完整代码
    //
    const getMember = async () => {
        return new Promise((resolve, reject) => {
            // 模拟获取会员数据接口异常
            setTimeout(() => {
                // 成功返回
                reject(new Error('会员接口异常'))
            }, 1000)
        })
    }
    
    const getGoods = async () => {
        return new Promise((resolve, reject) => {
            // 模拟接口获取商品数据
            setTimeout(() => {
                // 成功返回
                resolve(
                    [{
                        name: '面包',
                        price: 5
                    },{
                        name: '牛奶',
                        price: 6
                    }]
                )
            }, 2000)
        })
    }
    
    const init = async () => {
        // 加入.catch异常捕获
        const member = await getMember().catch((err) => {console.error("会员接口异常:", err)});
        if(member){
            console.log("会员:", member)
            // 处理会员业务逻辑...
        }
    
        // 加入.catch异常捕获
        const goods = await getGoods().catch((err) => {console.error("商品接口异常:", err)});
        if(goods){
            console.log("商品:", JSON.stringify(goods));
            // 处理商品业务逻辑...
        }
    
    }
    
    init();
    
    // 打印结果
    // 会员接口异常:xxxxx
    // 商品: [{"name":"面包","price":5},{"name":"牛奶","price":6}]
    
    

    方法三:使用 await-to-js插件库

    • await-to-js是什么

    await-to-js 是一个辅助开发者处理异步错误的库

    • await-to-js怎么下
    # npm安装
    npm i await-to-js --save
    # yarn安装
    yarn add await-to-js
    
    • await-to-js怎么写
    import to from 'await-to-js'
    const init = async () => {
        const [err, data] = await to(getMember())
        if (err){
            console.error("会员接口异常:", err)
            return
        }
        console.log("会员:", data)
    
        const [err1, data1] = await to(getGoods())
        if (err1){
            console.error("商品接口异常:", err1)
            return
        }
        console.log("商品:", JSON.stringify(data1))
    }
    
    // 完整代码
    import to from 'await-to-js'
    const getMember = async () => {
        return new Promise((resolve, reject) => {
            // 模拟获取会员数据接口异常
            setTimeout(() => {
                // 成功返回
                reject(new Error('会员接口异常'))
            }, 1000)
        })
    }
    
    const getGoods = async () => {
        return new Promise((resolve, reject) => {
            // 模拟接口获取商品数据
            setTimeout(() => {
                // 成功返回
                resolve(
                    [{
                        name: '面包',
                        price: 5
                    },{
                        name: '牛奶',
                        price: 6
                    }]
                )
            }, 2000)
        })
    }
    
    const init = async () => {
        const [err, data] = await to(getMember())
        if (err){
            console.error("会员接口异常:", err)
            return
        }
        console.log("会员:", data)
    
        const [err1, data1] = await to(getGoods())
        if (err1){
            console.error("商品接口异常:", err1)
            return
        }
        console.log("商品:", JSON.stringify(data1))
    }
    
    init();
    
    // 打印结果
    // 会员接口异常:xxxxx
    // 商品: [{"name":"面包","price":5},{"name":"牛奶","price":6}]
    
    
    • await-to-js源码分析
    /**
     * @param { Promise } promise
     * @param { Object= } errorExt - Additional Information you can pass to the err object
     * @return { Promise }
     */
    export function toError>(
      promise: Promise,
      errorExt?: object
    ): Promise<[U, undefined] | [null, T]> {
      return promise
        .then<[null, T]>((data: T) => [null, data])
        .catch<[U, undefined]>((err: U) => {
          if (errorExt) {
            const parsedError = Object.assign({}, err, errorExt)
            return [parsedError, undefined]
          }
    
          return [err, undefined]
        })
    }
    
    export default to
    

    分析一下:
    to方法入参是promise对象和自定义异常
    如果没有异常则直接返回[null, data],数组第一项是异常信息为null,第二项为正常数据
    如果捕获异常在catch中组装[err, undefined] 错误信息,数组第一项是异常信息为err,第二项数据为null


    总结

    本文通过async/await为切入点,介绍三种异步调用处理异常的方法:分别是try-catch、promise处理、await-to-js插件库处理。

    希望小伙伴能学以致用,精进代码的同时,也让别人看我们代码时,变得易读好上手,人如其码!


    🍈猜你想问

    如何与狗哥联系进行探讨

    关注公众号【JavaDog程序狗】

    公众号回复【入群】或者【加入】,便可成为【程序员学习交流摸鱼群】的一员,问题随便问,牛逼随便吹,目前群内已有超过200+个小伙伴啦,只能拉人入群啦!!!

    2.踩踩狗哥博客

    javadog.net

    大家可以在里面留言,随意发挥,有问必答


    🍯猜你喜欢

    文章推荐

    【规范】看看人家Git提交描述,那叫一个规矩

    【工具】用nvm管理nodejs版本切换,真香!

    【苹果】SpringBoot监听Iphone15邮件提醒,Selenium+Python自动化抢购脚本

    【项目实战】SpringBoot+uniapp+uview2打造H5+小程序+APP入门学习的聊天小项目

    【项目实战】SpringBoot+uniapp+uview2打造一个企业黑红名单吐槽小程序

    【模块分层】还不会SpringBoot项目模块分层?来这手把手教你!

    【ChatGPT】手摸手,带你玩转ChatGPT

    【ChatGPT】SpringBoot+uniapp+uview2对接OpenAI,带你开发玩转ChatGPT


  • 相关阅读:
    18亿欧元大动作,法国瞄准实现量子飞跃
    Vite+Vue3+Ant Design3.2报错: Cannot read properties of null (reading ‘isCE‘)
    k8s QoS与pod驱逐
    【网络爬虫 | Python】数字货币ok链上bitcoin大额交易实时爬取,存入 mysql 数据库
    Python字符串操作:str.format
    【OpenCV 例程200篇】206. Photoshop 色阶调整算法
    SfM详细流程介绍
    【华为OD机试真题 python】 绘图机器【2022 Q4 | 100分】
    致远OA wpsAssistServlet 任意文件上传漏洞
    《Java编程思想》读书笔记(三)
  • 原文地址:https://www.cnblogs.com/javadog-net/p/18215667