我们需要在异步任务中准确的进行错误捕获,以便我们可以知道错误出在什么地方

如果对Promise和trycatch不够理解的话,很多时候会出现Promise中的错误无法被捕获的情况,本文来讨论这些情况

try catch

try catch 只能捕获当前上下文中的错误,也就是只能捕获同步任务的情况,如下场景:

try {
    throw "程序执行遇到了一些错误";
} catch(e) {
    console.log(e)
}
// 控制台会输出:程序执行遇到了一些错误

这很好,错误被捕获了,我们可以在程序中进程错误的处理;

但是对于异步的任务,trycatch就显得无能为力,不能正确捕获错误:

try {
    setTimeout(() => {
      throw "程序执行遇到了一些错误"  
    })
} catch(e) {
    console.log(e);
}
// 控制台输出:Uncaught 程序执行遇到了一些错误;

又或者这样:

try {
    Promise.reject('程序执行遇到了一些错误');
} catch(e) {
    console.log(e);
}
// 控制台输出:Uncaught (in promise) 程序执行遇到了一些错误

上面的代码都无法正常捕获到错误,因为:trycatch永远捕获的是同步的错误

什么是同步的错误?

当在一个事件循环内,同一个任务队列中出现的错误,对于这个任务所在的上下文而言,就是同步错误。

setTimeoutPromise被称为任务源,来自不同的任务源注册的回调函数会被放入不同的任务队列中。

  • setTimeout中的任务会被放入宏任务

  • Promise中的任务会被放入微任务

    • 拓展:setTimeout是宿主浏览器发起的任务,一般会被放入宏任务
    • 而Promise是由JS引擎发起的任务,会被放入微任务

第一次事件循环中,JS引擎会把整个script代码当成一个宏任务执行,执行完成之后,再检测本次循环中是否存在微任务,存在的话就依次从微任务的任务队列中读取执行完所有的微任务,再读取宏任务的任务队列中的任务执行,再执行所有的微任务,如此循环。

JS的执行顺序就是每次事件循环中的宏任务-微任务的不断切换。

再看setTimeout中抛出的错误,这个错误已经不在trycatch所在的事件循环中了,所以这是一个异步错误,无法被trycatch捕获到。

同理,Promise.reject()此处虽然是同步执行的,但是此处reject的内容却在另一个微任务循环中,对于trycatch来讲也不是同步的,所以这两个错误都无法被捕获。

Promise.reject

要理解Promise.reject首先要了解它的返回值,Promise.reject返回的是一个Promise对象,请注意:是Promise对象

Promise对象在任何时候都是一个合法的对象,它不是错误也不是异常,所以在任何实现,直接对Promise.reject或者一个返回Promise对象的调用直接try catch是没有意义的,一个正常的对象永远不可能触发catch捕获。

假设我们由如下代码:

function getData() {
    Promise.reject('遇到了一些错误');
};
function click() {
    try {
        getData();
    } catch(e) {
        console.log(e);
    }
}
click(); // 我们模拟业务场景中的click事件
// 控制台输出: Uncaught (in promise) 遇到了一些错误

Promise已经通过reject抛出了错误,为什么try catch捕获不到呢?

首先,需要知道,对于一个函数的错误是否可以被捕获到,可以尝试将函数调用的返回值替换到函数调用出,看看是否为一个错误

上面getDate()调用会被替换为undefined

对于一个没有明确return的函数调用,其返回值永远是undefined的,所以代码如下:

function click() {
    try {
        undefined;
    } catch(e) {
        console.log(e);
    }
}

了解js基础的人肯定知道,这不算异常,这个代码会正常执行,不会走到catch中。

可能会有另一种思路,就是将Promise.reject返回出去,那么代码就变成:

function getData() {
  return Promise.reject('遇到了一些错误');
};
function click() {
    try {
        getData();
    } catch(e) {
        console.log(e);
    }
}
click();

Promise.reject返回的是一个Promise对象,它是对象,不是错误。所以在try catch中完成getData()调用后这里会出现一个Promise对象,这个对象是一个再正常不过的对象,不会被catch捕获,所以这个try catch依然是无效的。

于是,又出现一种思路:再调用处使用Promise的catch方法进行捕获,于是代码变成:

function getData() {
  return Promise.reject('遇到了一些错误');
};
function click() {
    try {
        getData().catch(console.log);
    } catch(e) {
        console.log(e);
    }
}
click();

这是可行的,rejext的错误可以被捕获,但这不是try catch的功劳,而是Promise的内部消化,所以这里的try catch依然没有意义。

解决Promise异常捕获

Promise异常是最常见的异步异常,其内部的错误基本都是被包装成了Promise对象后进行传递,所以解决Promise异步捕获整体思路有两个:

  • 使用Promise的catch方法内部消化;
  • 使用async和await将异步错误转同步错误再由try catch捕获

Promise.catch

对于Promise.reject中抛出的错误,或者Promise构造器中抛出的错误,亦或者then中出现的错误,无论是运行时还是通过throw主动抛出的,原则上都可以被catch捕获。

如下:

function getData() {
    Promise.reject('这里发生了错误').catch(console.log);
}
​
function click() {
    getData();
}
​
click();

亦或者在调用处捕获,但这需要被调用的函数能返回Promise对象;

function getData() {
    return Promise.reject('程序发生了一些错误');
}

function click() {
    getData().catch(console.log);
}
click();

上面两个方案都可行,事实上建议在业务逻辑允许的情况下,将Promise都返回出去,以便能向上传递,同时配合**unhandledrejection**进行兜底

async await 异步转同步

使用async和await可以将一个异步函数调用在语义上变成同步执行的效果,这样我们就可以使用try catch去统一处理。

例如:

function getData() {
    return Promise.reject('程序发生错误');
}
async function click() {
    try {
        await getData();
    } catch(e) {
        console.log(e);
    }
}

click();

需要注意的是,如果getData方法没有写return, 那么就无法将Promise对象向上传递,那么调用出的await等到的就是一个展开的undefined, 依旧不能进行错误处理。

注意事项

一个函数如果内部处理了Promise异步对象,那么原则上其处理结果应该也是一个Promise对象,对于需要进行错误捕获的场景,Promise对象应该始终通过return向上传递

兜底方案

一般情况下,同步错误如果没有进行捕获,那么这个错误所在的事件循环将终止,所以在开发阶段没有捕获的错误,使用一种方法进行兜底是很有必要的。

对于同步错误,可以定义window.onerror进行兜底处理,或者使用window.addEventListener('error', errHandler)来定义兜底函数。

对于Promise异常,则可以同步使用window.onunhandledrejection或者window.addEventListener('unhandledrejection', errHandler)来定义兜底函数。

我们再讨论then方法中的第二个参数和Promise.catch方法的区别

Promise中的then的第二个参数和catch有什么区别?

  • reject是用来抛出错误的,属于Promise的方法
  • catch是用来处理异常的,属于Promise实例的方法、
  • 区别

    主要区别就是,如果在then的第一个函数中抛出了异常,后面的catch能捕获到,但是then的第二个参数却捕获不到

    then的第二个参数和catch捕获信息的时候会遵循就近原则,如果是promise内部报错,reject抛出错误后,then的第二个参数和catch方法都存在的情况下,只有then的第二个参数能捕获到,如果then的第二个参数不存在,则catch方法会被捕获到。

题: then方法的连续调用,怎么能够知道是第几个then方法报错了呢。

new Promise((resolve,reject) => {
    setTimeout(() => {
        resolve(1);
    }, 1000)
}).then(res => {
    console.log(res);
    return new Promise((resolve,reject) => {
        reject('第一个then方法报错了');
    })
}).then(res => {
    console.log(res);
    return new Promise((resolve,reject) => {
		reject('第二个then方法报错了');
    })
}).catch(err => {
    console.log(err);
})

总结

到此这篇关于JavsScript中Promise错误捕获的文章就介绍到这了,更多相关JS Promise错误捕获内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

JavsScript中Promise的错误捕获详解的更多相关文章

  1. 详解koa2学习中使用 async 、await、promise解决异步的问题

    这篇文章主要介绍了详解koa2学习中使用 async 、await、promise解决异步的问题,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  3. 使用 JavaScript Promise 读取 Github 用户数据

    这篇文章主要介绍了使用JavaScript Promise读取Github用户数据,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下

  4. 浅谈PHP中的错误处理和异常处理

    下面小编就为大家带来一篇浅谈PHP中的错误处理和异常处理。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  5. Java如何主动从当前线程获取异常信息

    这篇文章主要介绍了Java如何主动从当前线程获取异常信息,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  6. Javascript的异步函数和Promise对象你了解吗

    这篇文章主要为大家详细介绍了Javascript异步函数和Promise对象,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助

  7. Java异常分类处理抛出捕获操作详解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  8. ES6的Promise用法详解

    本文详细讲解了ES6的Promise用法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  9. PHP中的异常及其处理机制

    这篇文章主要介绍了PHP中的异常及其处理机制,帮助大家更好的理解和学习使用PHP,感兴趣的朋友可以了解下

  10. 万字详解JavaScript手写一个Promise

    这篇文章主要介绍了万字详解JavaScript手写一个Promise,Promise就是一个类,在执行这个类的时候,需要传递一个执行器(回调函数)进去,执行器会立即执行

随机推荐

  1. js中‘!.’是什么意思

  2. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  4. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  6. JavaScript面向对象编程入门教程

    这篇文章主要介绍了JavaScript面向对象编程的相关概念,例如类、对象、属性、方法等面向对象的术语,并以实例讲解各种术语的使用,非常好的一篇面向对象入门教程,其它语言也可以参考哦

  7. jQuery中的通配符选择器使用总结

    通配符在控制input标签时相当好用,这里简单进行了jQuery中的通配符选择器使用总结,需要的朋友可以参考下

  8. javascript 动态调整图片尺寸实现代码

    在自己的网站上更新文章时一个比较常见的问题是:文章插图太宽,使整个网页都变形了。如果对每个插图都先进行缩放再插入的话,太麻烦了。

  9. jquery ajaxfileupload异步上传插件

    这篇文章主要为大家详细介绍了jquery ajaxfileupload异步上传插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. React学习之受控组件与数据共享实例分析

    这篇文章主要介绍了React学习之受控组件与数据共享,结合实例形式分析了React受控组件与组件间数据共享相关原理与使用技巧,需要的朋友可以参考下

返回
顶部