资讯专栏INFORMATION COLUMN

如何优雅地处理Async/Await的异常?

villainhr / 1811人阅读

摘要:能够捕获非异步的异常。来匹配正常异常的情况。在中处理所有的异常如果出错,则退出。所以,的模式使得异常处理变得非常简洁。自从年双十一正式上线,累计处理了亿错误事件,付费客户有阳光保险核桃编程荔枝掌门对微脉青团社等众多品牌企业。

译者按: 使用.catch()来捕获所有的异常

原文: Async Await Error Handling in JavaScript

译者: Fundebug

本文采用意译,版权归原作者所有

async/await 中的异常处理很让人混乱。尽管有很多种方式来应对async 函数的异常,但是连经验丰富的开发者有时候也会搞错。

假设你有一个叫做run()的异步函数。在本文中,我会描述 3 种方式来处理run()的异常情形: try/catch, Go 语言风格, 函数调用的时候使用 catch()(即run().catch())。 我会跟你解释为什么其实几乎只需要catch()就足够。

try/catch

当你第一次使用async/await, 你可能尝试使用try/catch将每一个 async 操作包围起来。如果你await一个被 reject 的 Promise,JavaScript 会抛出一个可以被捕获的错误。

run();

async function run() {
    try {
        await Promise.reject(new Error("Oops!"));
    } catch (error) {
        error.message; // "Oops!"
    }
}

try/catch 能够捕获非异步的异常。

run();

async function run() {
    const v = null;
    try {
        await Promise.resolve("foo");
        v.thisWillThrow;
    } catch (error) {
        // "TypeError: Cannot read property "thisWillThrow" of null"
        error.message;
    }
}

所以,只需要将所有的代码逻辑都用 try/catch包围起来就可以搞定?也不完全正确。下面的代码会抛出unhandled promise rejection. await将一个被拒绝的 promise 转换为可捕获的错误,但是 return 不行。

run();

async function run() {
    try {
        // 注意这里是return,不是await
        return Promise.reject(new Error("Oops!"));
    } catch (error) {
        // 代码不会执行到这里
    }
}

也不可能使用 return await来绕开。

还有一个缺点就是使用了try/catch 之后,就很难用.的语法来进行 Promise 链式组合了。

使用 Go 的语法

另一个常见的方式就是使用then()将一个本来需要用catch()来捕获并处理的 Promise 转换为普通的 Promise。然后像 Go 语言中一样,使用if(err)来处理异常。

run();

async function throwAnError() {
    throw new Error("Oops!");
}

async function noError() {
    return 42;
}

async function run() {
    // The `.then(() => null, err => err)` 来匹配正常/异常的情况。如果正常情况,返回`null`;如果异常,返回`err`
    let err = await throwAnError().then(() => null, err => err);
    if (err != null) {
        err.message; // "Oops"
    }

    err = await noError().then(() => null, err => err);
    err; // null
}

如果你真的想要同时返回 error 和正确的值,你可以完全假装在用 Go 语言。

run();

async function throwAnError() {
    throw new Error("Oops!");
}

async function noError() {
    return 42;
}

async function run() {
    // The `.then(v => [null, v], err => [err, null])` pattern
    // 你可以使用数组解构来匹配err和返回值
    let [err, res] = await throwAnError().then(
        v => [null, v],
        err => [err, null]
    );
    if (err != null) {
        err.message; // "Oops"
    }

    err = await noError().then(v => [null, v], err => [err, null]);
    err; // null
    res; // 42
}

使用 Go 语言风格的错误处理并不能摆脱return无法捕获的情况。而且还让整个代码更加的复杂,如果忘记if(err != null),就会出问题。

总的来说,有两大缺点:

代码极度重复,每一个地方都少不了if (err != null) ,真的很累,而且容易漏掉;

run()函数中的非异步的错误也无法处理;

总的来说,它并没有比try/catch好多少。

在函数调用的时候使用catch()

try/catch 和 Go 语言风格的异常处理都有各自的使用场景,但是处理所有异常最好的方法是在run()函数的后面使用catch(),像这样:run().catch()。换句话说,用一个catch()来处理run函数中的所有错误,而不是针对run里面的每一种情况都去写代码做相应的处理。

run()
    .catch(function handleError(err) {
        err.message; // Oops!
    })
    // 在handleError中处理所有的异常
    // 如果handleError出错,则退出。
    .catch(err => {
        process.nextTick(() => {
            throw err;
        });
    });

async function run() {
    await Promise.reject(new Error("Oops!"));
}

记住,async 函数总是返回 promise。只要函数中有异常,Promise 会 reject。而且,如果一个 async 函数返回的是一个 reject 的 Promise,那么这个 Promise 依然会继续被 reject。

run()
    .catch(function handleError(err) {
        err.message; // Oops!
    })
    .catch(err => {
        process.nextTick(() => {
            throw err;
        });
    });

async function run() {
    // 注意:这里使用了return,而不是await
    return Promise.reject(new Error("Oops!"));
}

为什么使用run().catch()而不是将整个run()函数用try/catch包起来呢?我们首先来考虑一个情况:如果try/catchcatch部分有异常,我们应该如何处理呢?只有一个方法:在catch里面接着使用try/catch。所以,run().catch()的模式使得异常处理变得非常简洁。

总结

我们最好是全局的有一个 errorHandler 来处理那些没有考虑到的异常,比如使用run().catch(handleError),而不是在run()函数里面所有可能出错的地方加上try/catch

关于Fundebug

Fundebug专注于JavaScript、微信小程序、微信小游戏、支付宝小程序、React Native、Node.js和Java线上应用实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了10亿+错误事件,付费客户有阳光保险、核桃编程、荔枝FM、掌门1对1、微脉、青团社等众多品牌企业。欢迎大家免费试用!

版权声明

转载时请注明作者 Fundebug以及本文地址:https://blog.fundebug.com/2019/07/24/async-await-error-handling-in-js/

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/105970.html

相关文章

  • Async/await学习

    摘要:这只是一个更优雅的得到值的语句,它比更加容易阅读和书写。总结放在一个函数前的有两个作用使函数总是返回一个允许在这其中使用前面的关键字能够使等待,直到处理结束。 Async/await 写在前面 渣渣新人的首篇外文文章翻译!!存在错误可能会很多,如有错误,烦请各位大大指正出来,感谢! 本篇为翻译!本篇为翻译!本篇为翻译! 原文文章地址:https://javascript.info/a...

    Tamic 评论0 收藏0
  • 「大概可能也许是」目前最好 JavaScript 异步方案 async/await

    摘要:使用时也要注意范围和层级。服务端配置服务端使用,最简单的方式是通过。云引擎是推出的服务器端运行环境,支持和环境,功能强大而且目前免费,结合,使原本复杂的开发工作变得简单高效。目前也支持和海外节点,轻松满足你的业务需求。 构建一个应用程序总是会面对异步调用,不论是在 Web 前端界面,还是 Node.js 服务端都是如此,JavaScript 里面处理异步调用一直是非常恶心的一件事情。以...

    Scorpion 评论0 收藏0
  • JS 异步编程六种方案

    摘要:接下来介绍下异步编程六种方法。六生成器函数是提供的一种异步编程解决方案,语法行为与传统函数完全不同,最大的特点就是可以控制函数的执行。参考文章前端面试之道异步编程的种方法你不知道的中卷函数的含义和用法替代的个理由 前言 我们知道Javascript语言的执行环境是单线程。也就是指一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务。 这种模式虽然实现起...

    longmon 评论0 收藏0
  • JavaScript 优雅实现方式包含你可能不知道知识点

    摘要:优点简单粗暴,直接调用缺点兼容性不太好,不过的话都支持你可能不知道的前端知识点对象和的方法。下面从深层次剖析一下对于开始的两个赋值语句,,,相当于,而显然等于。同理可以分析第三个赋值语句 有些东西很好用,但是你未必知道;有些东西你可能用过,但是你未必知道原理。 实现一个目的有多种途径,俗话说,条条大路通罗马。很多内容来自平时的一些收集以及过往博客文章底下的精彩评论,收集整理拓展一波,发...

    617035918 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<