异步编程中的错误处理
2019-05-30 本文已影响0人
回调的幸福时光
前言
在项目中,promise 的应用基本是为了解决异步编程,例如 ajax 请求。而在这个场景下,有个很常见的需求就是捕获请求异常。本篇文章以此为起点,梳理归纳异常错误分类。
一、Promise 中的错误处理
常见错误分类
- 编码错误
此种就是开发过程中,编码有误。
let promise = new Promise(function(resolve, reject) {
const a = 0;
a = 10;
});
promise.catch(function(error) {
console.log('这里打印的错误:',error.message);
});
- 主动抛出错误
业务场景:请求结果的 http 状态值是 4xx、5xx,或者代表业务的 code 值是错误的,需要抛出异常。
在构造函数内部包含一个隐式的 try-catch
,因此内部的错误会被捕捉并被传入 rejection 处理。
let promise = new Promise(function(resolve, reject) {
throw new Error("出错啦~");
});
promise.catch(function(error) {
console.log('这里打印的错误:',error.message);
});
等同于以下:
let promise = new Promise(function(resolve, reject) {
try {
throw new Error("出错啦~");
} catch (err) {
reject(err);
}
});
promise.catch(function(error) {
console.log('这里打印的错误:',error.message);
});
-
使用 reject 抛出异常比 throw 更好
通过 throw 抛出的错误在同步任务中能够被 catch ,但如果在异步中, promise 将无法捕获。
运行以下例子可发现,无法捕获到错误。
let promise = new Promise(function(resolve, reject) {
setTimeout(() => {
throw new Error("出错啦~");
},20)
});
promise.catch(function(error) {
console.log('这里打印的错误:',error.message); // 无法捕获到错误
});
原因:虽然 promise 的构造函数中有隐含的 try-catch ,但因为setTimeout() 是一个异步任务, 所以它的回调函数执行的时机要比 try-catch 中的 catch 语句晚,导致并没有 regect() 出去错误,所以导致 promise 无法通过 catch 捕获异常。
let promise = new Promise(function(resolve, reject) {
try {
setTimeout(() => { // 回调函数执行的晚
throw new Error("出错啦~");
},20)
} catch (err) {
reject(err); // 没有执行
}
});
所以这里推荐使用 reject 替代 throw。如下所示:
let promise = new Promise(function(resolve, reject) {
setTimeout(() => {
reject(new Error("出错啦~"));
},20)
});
- 在resolve()后面抛出的错误会被忽略
let promise = new Promise(function(resolve, reject) {
setTimeout(() => {
resolve()
reject(new Error("出错啦~"));
},20)
});
promise.catch(function(error) {
console.log('这里打印的错误:',error.message); // 无法捕获到错误
});
- promise 链的异常
运行以下例子:
let promise = Promise.resolve();
promise.then(() => {
return Promise.reject("first");
}).then(() => {
console.log("two 没执行");
return Promise.reject('two');
}).catch((err) => {
console.log(err); // first
}).then(() => {
console.log("catch 返回的是一个 promise 对象"); // catch 返回的是一个 promise 对象
return Promise.reject('three');
}).catch((err) => {
console.log(err); // three
})
- reject 抛出错误之后,后续的 then 不再执行,错误被最近的一个 catch 捕获到
- catch 返回一个 promise
- 嵌套 promise 中的 reject
此点源自 [译]深入Promise错误处理
let p2 = new Promise(function(resolve, reject) {
reject('我是内部的 promise 抛出的错误')
})
let p1 = new Promise(function(resolve, reject) {
resolve(p2)
})
p1.catch(err => {
console.log(err)
})
二、Genertor 中的错误处理
- 函数内部异常
在外部通过 try-catch 语句捕获错误。
function *gen() {
const a = 1;
yield a = 2; // 故意写错
}
const task = gen();
try {
task.next();
} catch (e) {
console.log('捕获到错误了:',e);
}
- 通过生成器的
throw()
抛出异常
在生成器函数内部,通过 try-catch 捕获错误。
- 当异常被捕获后,Generator 函数会继续向下执行,直到遇到下一个 yield 操作并输出 yield 后面表达式的值。
function *gen() {
try {
yield console.log(1);
} catch (e) {
console.log(e);
}
yield console.log(2);
}
const task = gen();
task.next();
task.throw("抛出了异常")
注意 try-catch 的位置,即抛错的位置应该和 try-catch 大致吻合,否则仍旧无法捕获到。

参考
developer.mozilla Promise.catch()
《Understanding ECMAScript 6》(简体中文版)Promise 与异步编程
《Understanding ECMAScript 6》(简体中文版)迭代器与生成器