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

js,await · 浏览次数 : 5

小编点评

**方法一:使用 try-catch 异常捕获** ```javascript const member = await getMember(); console.log("\n会员:\n", member); const goods = await getGoods(); console.log("\n商品:\n", JSON.stringify(goods)); ``` **方法二:使用 Promise 处理** ```javascript 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); }); }; init(); ``` **方法三:使用 await-to-js 插件库** ```javascript import to from 'await-to-js'; const init = async () => { try { const [err, data] = await to(getMember()); console.log("\n会员:\n", data); const [err1, data1] = await to(getGoods()); console.log("\n商品:\n", JSON.stringify(data1)); } catch (error) { console.error(error); } }; ```

正文

🍊缘由

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 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) {
        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


与【JS】await异常捕获,这样做才完美相似的内容:

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

文章关注JavaScript中async/await的异常处理,指出未捕获异常的潜在风险。1)使用try-catch,虽全面但冗余;2)借助Promise的catch,减少冗余; 3) 利用await-to-js库简化异常处理

第126篇: 异步函数(async和await)

好家伙,本篇为《JS高级程序设计》第十章“期约与异步函数”学习笔记 ES8 的 async/await 旨在解决利用异步结构组织代码的问题。 为为此增加了两个新关键字:async 和 await。 1.async关键字 1.1.使用说明 async 关键字用于声明异步函数。 函数声明、函数表达式、箭

如何在forEach内使用异步调用 async/await

翻自: How to use async and await in a forEach JS loop? https://learn.coderslang.com/0144-how-to-use-async-and-await-in-a-foreach-js-loop/ 事实上我们无法在 forEa

blazor优雅的方式导入组件相关的js脚本

基本的组件导入方式为: 1 await JsRuntime.InvokeVoidAsync("import", $"XXXXX.js"); 优雅的组件导入方式: 1 await JsRuntime.ImportAsync(); 这种优雅一点不难,只需要写一个扩展方法,把他放在任

js定时器

一.定时器 1. JS存在两种定时器 setTimeout() 延迟定时器 setInterval() 循环定时器(“间隔器”) 定时器中的函数挂载在window对象,内部的this ——> window setTimerout(function(){ console.log('wuwei') },

JS神奇的或0(|0)

按照常识,位运算x|0,要么等于x,要么等于0 那么在JS的世界你的认知就要被颠覆了 下面请看 不带或0运算: (window.crypto.getRandomValues(new Uint32Array(1))[0] * 0x10000 ) 168546249998336 (window.cryp

调用了这么久的JS方法是长在对象、类、值本身还是原型链上?

调用了这么久的JS方法是长在对象、类、值本身还是原型链上? JavaScript这门语言总是能带给我惊喜,在敲代码的时候习以为常的写法,退一步再看看发现自己其实对很多基操只有表面的使用,而从来没思考过为何要这样操作。 今天整理JS代码的时候突然发出灵魂三连问: 为什么有些时候操作对象,可以直接调用对

什么是浅拷贝和深拷贝,如何用 js 代码实现?

〇、简介和对比 简介 浅拷贝:只复制原始对象的第一层属性值。 如果属性值是值类型,将直接复制值,本值和副本变更互不影响; 如果是引用数据类型,则复制内存地址,因此原始对象和新对象的属性指向相同的内存地址,改变任一值,另一变量值也会同步变更。 深拷贝:递归地复制原始对象的所有层级。 每一个属性值都会在

记录工作中常用的 JS 数组相关操作

工作中难免会遇到各种各样的数据结构,较为全面的了解数组操作,对于复杂数据结构的处理会非常有用且节省时间。所以想在这里总结一下工作中常用的数组操作,都是一些非常基础的知识,大家看个乐就好~

【技巧】JS代码这么写,前端小姐姐都会爱上你

这篇文章分享了JavaScript编程中的实用技巧,包括解构赋值的短路语法避免错误、深度解构及默认值设定,以及数组操作如条件添加元素、获取最后一个元素和使用includes优化条件判断。此外,还介绍了从URL解析参数、页面滚动功能和获取滚动距离的JS片段。作者提倡使用这些技巧提升代码质量和效率,并邀...