关于 promise 吃到错误的理解

下面的内容需要对浏览器原生支持的 promise 的基本用法有了解,如果你还不知道 promise 和 promise 的 catch 方法,你可能需要先在 这里 了解一下。

在 阮一峰大神的 《ECMAScript 6 入门》 关于 Promise 对象那一章在介绍 Promise.prototype.catch() 方法时,里面有一句描述是这样写的 :

跟传统的try/catch代码块不同的是,如果没有使用catch方法指定错误处理的回调函数,Promise 对象抛出的错误不会传递到外层代码,即不会有任何反应。

一开始我 错误的理解 了这句话的意思,认为是在 promise 内部如果发生错误的话,可以使用 catch 方法来捕获错误,但是如果不使用 catch 方法,它会静悄悄的,不会有报错,就像加了 try-catch 但是在 catch 里面不进行任何处理。

然后我做了一些测试发现和我想的不一样:

首先,试一下在 promise 里面抛出一个异常,看看能不能通过 catch 捕获到

  1. var promise = new Promise(function(resolve, reject) {
  2. throw new Error('test'); // 这里抛了一个异常
  3. });
  4. promise
  5. .then(function(value) { console.log(value) })
  6. .catch((err) => console.log('promise catch err'))
  7. // 控制台输出:
  8. // promise catch err

没什么问题,catch 方法成功捕获到异常了,但是这个异常会不会再被抛到外面去呢?为了以防万一,我们在window 对象上加个 onerror 事件监测一下看看

  1. window.onerror = function () {
  2. console.log('window err')
  3. }
  4. var promise = new Promise(function(resolve, reject) {
  5. throw new Error('test');
  6. });
  7. promise
  8. .then(function(value) { console.log(value) })
  9. .catch((err) => console.log('promise catch err'))
  10. // 控制台输出:
  11. // promise catch err

结果还是一样,说明 catch 方法成功捕获到异常而且没有继续往外面抛,这和书里说的一样

那如果去掉 catch 方法呢?我们继续往下看

  1. var promise = new Promise(function(resolve, reject) {
  2. throw new Error('test');
  3. });
  4. promise.then(function(value) { console.log(value) }) // 这里不捕获异常了
  5. // 控制台输出:
  6. // Uncaught (in promise) Error: test

这里控制台输出了一个报错:Uncaught (in promise) Error: test,咦,如果像书里说的:

跟传统的try/catch代码块不同的是,如果没有使用catch方法指定错误处理的回调函数,Promise 对象抛出的错误不会传递到外层代码,即不会有任何反应。

上面说不会有任何反应,那怎么控制台还会报错呢?

我们可以猜测一下,其实这里有点像使用 try-catch 那样捕获了异常然后打印出错误信息后就不再做其他处理一样:

  1. try{
  2. console.log(x)
  3. }catch(err){
  4. console.error(err)
  5. }

然后我们加一个 window.onerror 发现,这时 promise 外面添加错误监听事件不会捕获到 promise 对象里面没有进行捕获的错误,像下面这样:

  1. window.onerror = function () { // 我们添加了 window 的 onerror 处理函数
  2. console.log('window err')
  3. }
  4. promise = new Promise(function(resolve, reject) {
  5. throw new Error('test');
  6. });
  7. promise.then(function(value) { console.log(value) })
  8. // 控制台输出:
  9. // Uncaught (in promise) Error: test

可以看到 window 的错误处理事件并没有被触发,所以报错应该是 promise 内部捕获处理的时候直接打印的而没有被抛出,也就验证了我们上面的猜测。

所以到这里,我们可以把书上那句话重新翻译一下得到一条结论

promise 对象里面同步代码抛出的错误在没有通过 promise 的 catch 方法捕获时是会打印报错的(不会阻止 promise 外面代码的执行),但是不会传递到外面触发其他错误监听函数(比如 window.onerror 、try-catch 等)


书上还讲到一个东西也挺奇怪的

  1. window.onerror = function () {
  2. console.log('window err')
  3. }
  4. var promise = new Promise(function (resolve, reject) {
  5. setTimeout(function () {
  6. throw new Error('test')
  7. }, 0)
  8. resolve('ok');
  9. });
  10. promise
  11. .then(function (value) { console.log(value) })
  12. .catch(() => console.log('promise catch err'))

根据我们上面的理解,你觉得这个控制台会显示什么东西呢?

如果你对答案是 promise catch err,那你就错了,他的结果是:

  1. // 控制台输出:
  2. // ok
  3. // window err
  4. // Uncaught Error: test

这里由于是在 setTimeout 里面抛出错误的,所以报错会在同步代码执行完后的下一轮 “事件循环” 里执行,也就是说当 setTimeout 里面的函数执行后报错时,promise 已经执行完了(所以就算 resolve('ok') 写在 setTimeout 下面也是先输出 ok),所以这个错误是在 Promise 函数体外抛出的,当然也就不会被 promise 的 catch 方法捕获,所以就会传到 window 上被捕获并输出 window err,然后再被浏览器捕获输出Uncaught Error: test,如果在 window onerror 处理程序里面 return true,就不会看到浏览器捕获输出的 Uncaught Error: test 报错。

再看一下 下面的两个例子:

这里是在 promise 里面同步执行了 throw Error 和 resolve 两个操作

  1. window.onerror = function () {
  2. console.log('window err');
  3. }
  4. var promise = new Promise(function (resolve, reject) {
  5. throw new Error('test');
  6. resolve('ok');
  7. });
  8. promise.then(function (value) { console.log(value) })
  9. .catch(() => console.log('promise catch err'))
  10. // 控制台输出:
  11. // promise catch err

这里是在 promise 里面用 setTimeout 异步执行了 throw Error 和 resolve 两个操作

  1. window.onerror = function () {
  2. console.log('window err');
  3. }
  4. var promise = new Promise(function (resolve, reject) {
  5. setTimeout(function () { // 这里包了一个 setTimeout
  6. throw new Error('test');
  7. resolve('ok');
  8. }, 0)
  9. });
  10. promise.then(function (value) { console.log(value) })
  11. .catch(() => console.log('promise catch err'))
  12. // 控制台输出:
  13. //window err
  14. //Uncaught Error: test

可以看到第二个例子中就算 promise 的状态还是 pedding ,异步操作里面的报错也不会被 promise 的 catch 方法捕获

所以这里又可以得到一条结论

Promise.prototype.catch() 方法对错误处理和捕获的规则只对 promise 里面的同步执行代码有效,如果此时 promise 里面有异步操作出错的话,是不受 promise 这些规则限制的,而是像正常的报错一样处理。

总结:

  1. promise 对象里面同步代码抛出的错误在没有通过 promise 的 catch 方法捕获时是会打印报错的(不会阻止 promise 外面代码的执行),但是不会传递到外面触发其他错误监听函数(比如 window.onerror 、try-catch 等)
  2. Promise.prototype.catch() 方法对错误处理和捕获的规则只对 promise 里面的同步执行代码有效,如果此时 promise 里面有异步操作出错的话,是不受 promise 这些规则限制的,而是像正常的报错一样处理。
  3. 其实上面两条规则可以看出 promise 对错误的处理应该是在内部使用了像 try-catch 的方式处理了错误,所以异步的它是处理不了的。就行下面这样一样,try-catch 并不会捕获到错误,错误会被 window.onerror 捕获
  1. window.onerror = function () {
  2. console.log('window err');
  3. return true
  4. }
  5. try{
  6. setTimeout(function () {
  7. console.log(x) // 这里 x 未定义
  8. }, 10)
  9. }catch(err){
  10. console.log('try err')
  11. }
  12. // 控制台输出:
  13. //window err

最后,可能还会有一些理解上的错误,希望大家多多指正。

关于 promise 吃到错误的理解的更多相关文章

  1. 错误的理解引起的bug async await 执行顺序

    今天有幸好碰到一个bug,让我知道了之前我对await async 的理解有点偏差. 错误的理解 之前我一直以为  await 后面的表达式,如果是直接返回一个具体的值就不会等待,而是继续执行asyn ...

  2. webpack+babel项目在IE下报Promise未定义错误引出的思考

    低版本浏览器引起的问题 最近开发一个基于webpack+babel+react的项目,一般本地是在chrome浏览上面开发,chrome浏览器开发因为支持大部分新的js特性,所以一般不怎么需要poly ...

  3. angularJS中的Promise对象($q)的深入理解

    原文链接:a better way to learn AngularJS - promises AngularJS通过内置的$q服务提供Promise编程模式.通过将异步函数注册到promise对象, ...

  4. Uncaught (in promise) TypeError:的错误

    1.错误 创建一个vue实例,在data定义一些变量,如activityTime. 在methods里面用了axios发送请求. 在then的回调里使用this.activityTime 报错! 2. ...

  5. 关于EINTR错误的理解【转】

    转自:http://www.xuebuyuan.com/1470645.html 最近在工作中遇到了EINTR错误,感到比较困惑,几番研究之后,颇有心得和收获,特记录如下,便于以后查询,也给有同样困惑 ...

  6. Java中的static(1)【持续更新】——关于Eclipse的No enclosing instance of type ... 错误的理解和改正

    No enclosing instance of type SomeClass is accessible. Must qualify the allocation with an enclosing ...

  7. 对express中引入文件时提示Error: Cannot find module错误的理解

    打算写个小demo,在引入一个routes文件时,一直提示Error: Cannot find module('./routes')的错误,经过一番了解. 如果要把整个文件夹下所有的模块都引进来  v ...

  8. OpenStack学习系列-----第二篇 由一个错误看理解整个架构的重要性

    看了openstack没几天,然后就开始试着用Java调用所有的API,第一步得到Credentials的时候成功了,然后第二步,传参数使所有的server信息都列出来的时候报错404.具体描述如下( ...

  9. Promise里捕捉错误的最佳实践

    Promise里的同步部分不需要try catch new Promise((resolve, reject) => { throw new Error('error'); setTimeout ...

随机推荐

  1. Batch Normalization&Dropout浅析

    一. Batch Normalization 对于深度神经网络,训练起来有时很难拟合,可以使用更先进的优化算法,例如:SGD+momentum.RMSProp.Adam等算法.另一种策略则是高改变网络 ...

  2. qml 静态编译程序执行错误 无法定位程序输入点 CreateDXGIFactory2 于动态链接库 dxgi.dll 上

    重新编译 qt 静态库即可,或 删除该动态库. -no-feature-d3d12 解决方案请参考如下网址: https://forum.qt.io/topic/78380/entry-point-n ...

  3. 如何使用 VS2015 进行远程调试?

    VisualStudio\Microsoft Visual Studio 14.0\Common7\IDE\Remote Debugger 直接复制 Remote Debugger 文件,里面包含了 ...

  4. javascript正则表达式的一些笔记

    正则表达式:Regular Expression.使用单个字符串来描述,匹配一系列符合某个句法规则的字符串.即按照某种规则去匹配符合条件的字符串.正则表达式就是规则. \b 单词边界 regexp对象 ...

  5. 项目实战14—ELK 企业内部搜索引擎

    一.els.elk 的介绍 1.els,elk els:ElasticSearch,Logstash,Kibana,Beats elk:ElasticSearch,Logstash,Kibana ① ...

  6. C++ 监测磁盘空间

    硬盘管理器 头文件 HardDiskManager.h : #if _MSC_VER > 1000 #pragma once #endif #include <windows.h> ...

  7. Java线程和守护进程

    ava的线程机制,有两类线程:User Thread(用户线程).Daemon Thread(守护线程) . 操作系统里面是没有守护线程的概念,只有守护进程,但是Java语言机制是构建在JVM的基础之 ...

  8. Centos中hive/hbase/hadoop/mysql实际操作及问题总结

    目录 Hive中文乱码问题 hive和hbase的版本不一致 Ambari hive插入Hbase出错 Hive0.12和Hbase0.96不兼容,重新编译hive0.12.0 hiveserver不 ...

  9. 01-python中字符串的常见操作

    (1)find 检测str是否包含在myStr中,如果存在则返回开始的索引值,否则返回-1. In [1]: myStr = "hello world tairan and tairanCi ...

  10. JFFS2 文件系统及新特性介绍

    简介: JFFS2 是一个开放源码的项目(www.infradead.org). 它是在闪存上使用非常广泛的读/写文件系统,在嵌入式系统中被普遍的应用.这篇文章首先分析了在闪存上使用 JFFS2 的必 ...