response.end后抛了异常_error(in promise) 数量异常的分析

在方法内使用 promise ,罕见的,可能导致抛出的 error 数量与预期不符合。这篇文章旨在构造这样的特殊案例,并尝试对 promise 抛异常的原理进行分析,而非介绍如何捕获异常。

如果我的理解存在偏差,欢迎讨论或勘误(Tim: 911120823)

控制台显示 error(in promise) 的原因

尽管处理 error(in promise) 的方式,与普通的 error 大相径庭,promise 内抛出的错误,无法被常规的 window.onerror 等方式捕获。但既然是异常,由异常上抛机制逐层传递,必然与 object 之类的东西无关(看起来是废话,但把 'object' 替换成 promise 就能发现,异常数量不符合预期,是因为我的下意识认为,其数量与 promise 数量直接相关)。

Promise.resolve 与 Promise.reject

Promise.resolve(value) ,如果 value 是 promise,value 充当返回值(即,不会生成新的异步操作步骤或链)。否则返回成功状态的 promise。

Promise.reject(reason) ,参数只是描述失败的原因。表示异步操作链执行失败,隐式的在异步操作链中(非同步)上抛异常;

案例及分析

方法返回 rejected promise

// 异常数量:1
const rejectedP = Promise.reject('-');
const fun = p => p;

rejectedP.finally(fun(rejectedP));

显而易见,调用 Promise.reject 会在异步操作链中上抛异常,但是方法返回 rejected promise,并不会产生新 error

一个 rejected promise 派生多个 promise

// 异常数量:1
const p0 = Promise.reject('-');
const p1 = p0.then(() => {});
const p2 = p1.then(() => {});

23b434a45bc964df738744f5c85e4d3e.png

三个有线性关系的 promise,却只抛出一个异常。error 数量与 promise 数量不存在直接关系,如果没有产生新的异步操作链,多个 rejected promise 对应一个 error。

// 异常数量:2
const p0 = Promise.reject('-');
const p1 = p0.then(() => {});
const p2 = p0.then(() => {});

0bb8c608091ce4d953d4a980de96b1ae.png

仅仅是把 p1、p2 对应的异步操作链中的第一步,改成 p0 对应的异步操作,异常数量立马+1。

可见除了遵循传统的异常逐层上抛机制,error(in promise)遇到异步操作链的分叉节点,会在不同的异步操作链分别上抛异常。即 error 数量与异步操作链数量存在直接关系

async function 情形

// 异常数量:1
const test = () => {
  const rejectedP = Promise.reject('-');

  rejectedP.finally(() => {});

  return rejectedP;
}

// 异常数量:2。分析图如下
const asyncTest = async () => {
  const rejectedP = Promise.reject('-');

  rejectedP.finally(() => {});

  return rejectedP; // promiseEx
} // promiseIm

107c77a4d8b04904fd8cc1cfd0843314.png

对于这个现象我还只有一点猜测,如果有谬误请联系我

mdn 上提到 A Promise which will be resolved with the value returned by the async function,容易引起误解。经过测试,这里并不是直接调用 Promise.resolve() 方法。如果是的话,下面的例子 P0 应该等于 p1。

const p0 = Promise.resolve(1);
const p1 = (async () => p0)();

p0 === p1; // false

而是调用了内部定义的 resolve 方法 包装显式返回值 promiseEx。不同于 Promise.resolve ,隐式返回了新的 promiseIm,代理新的异步操作链(原因稍后解释)。且 promiseIm 的状态除了受显式返回值 promiseEx 终态的影响,还与方法内部代码有关(只要内部抛错 promiseIm 即 rejected)。

promiseIm 代理的是从调用 async function 起,一直到方法返回,同步逻辑与 await 对应的异步逻辑,再加上返回值 promiseEx 代理的异步操作链。

下面举出 promiseIm 与 promsieEx 不完全等价的例子。

// 异常数量:1。

/**
 * 新生成的 promiseIm 代理的链只是 promiseEx 对应链在无分叉的扩展。虽然不等价,但是并不会制造新的分支。
 */
const extendP = async () => {
  await Promise.resolve(); // resolveP

  return Promise.reject('-'); // rejectP / promiseEx
} // promiseIm

b0273a791d85b3fca6845af41e926716.png

我猜测,正因为两者不完全等价,所以 async function 需要生成新的包装过的异步操作链。

回过头来看,或许正是没有笼统的通过 Promise.resolve() 处理 promiseEx 充当 async function 返回值的原因。

总结

  • promise 是异步操作结果的代理对象,当 promise 代理的异步操作链失败,1 个 error(in promise) 被抛出;
  • error(in promise) 抛出后,进入 promise 状态回调并逐层上抛,直到抵达顶层变为 uncaught error(in promise);
  • 根据异常上抛机制,error 数量与实际的异步操作链数量相关。如果在上抛途中,存在新的异步操作链,生成新 error;
  • async 方法隐式返回的 promiseIm 代理的新异步操作链,如果与显式返回值 promiseEx 代理的链分叉,异步操作链数+1,多抛出一次错误

补充

vue 处理实例方法返回的 promise

vue 实例定义的方法会被调用,得到的返回值追加 .catch,异常时手动进入错误收集器。因此要注意,如果在方法内生成了新的 promise,一定要返回新的 promise,否则异常抛出无法被 vue 的错误收集器监听到。

662336310a1c8e23eb72e775b813c86f.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值