本篇内容介绍了“JavsScript中Promise的错误捕获问题怎么解决”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
我们需要在异步任务中准确的进行错误捕获,以便我们可以知道错误出在什么地方
如果对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永远捕获的是同步的错误
什么是同步的错误?
当在一个事件循环内,同一个任务队列中出现的错误,对于这个任务所在的上下文而言,就是同步错误。
setTimeout和Promise被称为任务源,来自不同的任务源注册的回调函数会被放入不同的任务队列中。
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的错误捕获问题怎么解决”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!