Jacky's blog Jacky's blog
首页
  • 编码专题
  • 深入浅出 Vite
  • 深入浅出 babel
  • 快速上手API
  • 深入浅出 react
  • Node

    • code-notebook
  • 状态管理

    • redux
  • 前端工程化

    • Wepack
  • React源码

    • React源码
  • 组件库封装

    • 组件库
  • 开发工具

    • Vscode 插件
  • 项目展示
  • 案例中心 (opens new window)
  • First Project
  • 基础算法题
  • 链表题
  • 动态规划
  • 双指针
  • 递归
  • 数据结构
  • 前端学习计划 (opens new window)
  • 技术随笔
  • 转载文章
  • 包管理工具
  • 前端学习周报
  • VSCode插件
  • Promise 专题
  • 函数技巧
  • React 专题
  • 配置文件

    • TSCONFIG-配置 (opens new window)
    • NGINX-配置 (opens new window)
    • 正则规则查询手册 (opens new window)
    • Lint 配置 (opens new window)
  • 教程

    • GIT-教程
    • NPM SCRIPTS-工作流 (opens new window)
    • DOCKER-教程 (opens new window)
    • LERNA-教程 (opens new window)
    • GIT-常用操作整理 (opens new window)
  • VSCode

    • LAUNCH.JSON (opens new window)
  • 指令

    • NPM 指令 (opens new window)
    • NVM 指令 (opens new window)
    • Nginx 指令 (opens new window)
    • YARN 指令 (opens new window)
    • PNPM 指令 (opens new window)
  • 库

    • FS-EXTRA 库 (opens new window)
    • NODE 库-PATH (opens new window)
  • 永远的神

    • 魔法师卡颂-自顶向下学 React 源码 (opens new window)
    • 全栈潇晨 (opens new window)
    • 博客-程序员山月-Daily (opens new window)
    • 淘系前端:冴羽 (opens new window)
  • 系列文章

    • 《图解HTTP》 (opens new window)
    • 《ES6标准入门》 (opens new window)
    • 《现代JavaScript教程》 (opens new window)
    • 《深入浅出Webpack》 (opens new window)
    • VSCode 插件系列:小茗同学 (opens new window)
    • JEST 教程 (opens new window)
    • 前端精读周刊:各种精读系列 (opens new window)
    • 一文吃透系列 (opens new window)
    • 图解 REACT 原理 (opens new window)
  • 实用网站

    • MDN (opens new window)
    • CAN I USE (opens new window)
    • TYPESCRIPT-ESLint-RULES (opens new window)
    • ESLint-RULES (opens new window)
    • FRONT-END TREND (opens new window)
    • NPM TREND (opens new window)
    • 在线分析 Node 依赖 (opens new window)
    • FIND NPM (opens new window)
    • CODE PEN (opens new window)
    • 印记中文 (opens new window)
    • TOOL.LU (opens new window)
    • 阮一峰-网道 (opens new window)
    • DIGITAL OCEAN (opens new window)
    • DEVDOCS.IO (opens new window)
    • JOI (opens new window)
  • 算法

    • 小浩算法 (opens new window)
    • LABULADONG 的算法小抄 (opens new window)
    • 力扣 SOLUTION (opens new window)
    • HACKER RANK (opens new window)
    • 代码随想录 (opens new window)
  • 博客系列

    • 美团大佬 (opens new window)
    • 蜡笔小伟 (opens new window)
    • 优秀博客1 (opens new window)
    • 优秀博客2-umi (opens new window)
    • 优质博客 (opens new window)
  • CSS

    • CSS-EASING 库 (opens new window)
    • ROUGH.JS (opens new window)
    • CSS 网站收集
    • UNOCSS (opens new window)
  • 前端

    • PROMISE (opens new window)
    • UNDERSCORE.JS (opens new window)
    • study with BGM (opens new window)
    • nginx【B站视频】 (opens new window)
    • 机器学习
    • Js基础
  • 掘金已购课程

    • 前端自动化测试精讲 (opens new window)
    • 深入浅出 Vite (opens new window)
    • 现代 Web 布局 (opens new window)
    • 前端算法与数据结构 (opens new window)
    • 基于 Vite 的 SSG 框架开发实战 (opens new window)
    • SSR 实战:官网开发指南 (opens new window)
    • WebGL 入门与实践 (opens new window)
    • 玩转 CSS 的艺术之美 (opens new window)
    • 前端调试通关秘籍 (opens new window)
    • React 进阶实践指南 (opens new window)
    • TypeScript 全面进阶指南 (opens new window)
    • 前端缓存技术与方案解析 (opens new window)
    • npm scripts 前端工作流 (opens new window)
    • Webpack5 核心原理与应用实践 (opens new window)
  • 购物车

    • 张鑫旭-技术写作指南 (opens new window)
    • 深入剖析 Node.js 底层原理 (opens new window)
    • 前端开发者的现代 C++ 课 (opens new window)
    • 从前端到全栈 (opens new window)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Jacky Wang

行到水穷处,坐看云起时
首页
  • 编码专题
  • 深入浅出 Vite
  • 深入浅出 babel
  • 快速上手API
  • 深入浅出 react
  • Node

    • code-notebook
  • 状态管理

    • redux
  • 前端工程化

    • Wepack
  • React源码

    • React源码
  • 组件库封装

    • 组件库
  • 开发工具

    • Vscode 插件
  • 项目展示
  • 案例中心 (opens new window)
  • First Project
  • 基础算法题
  • 链表题
  • 动态规划
  • 双指针
  • 递归
  • 数据结构
  • 前端学习计划 (opens new window)
  • 技术随笔
  • 转载文章
  • 包管理工具
  • 前端学习周报
  • VSCode插件
  • Promise 专题
  • 函数技巧
  • React 专题
  • 配置文件

    • TSCONFIG-配置 (opens new window)
    • NGINX-配置 (opens new window)
    • 正则规则查询手册 (opens new window)
    • Lint 配置 (opens new window)
  • 教程

    • GIT-教程
    • NPM SCRIPTS-工作流 (opens new window)
    • DOCKER-教程 (opens new window)
    • LERNA-教程 (opens new window)
    • GIT-常用操作整理 (opens new window)
  • VSCode

    • LAUNCH.JSON (opens new window)
  • 指令

    • NPM 指令 (opens new window)
    • NVM 指令 (opens new window)
    • Nginx 指令 (opens new window)
    • YARN 指令 (opens new window)
    • PNPM 指令 (opens new window)
  • 库

    • FS-EXTRA 库 (opens new window)
    • NODE 库-PATH (opens new window)
  • 永远的神

    • 魔法师卡颂-自顶向下学 React 源码 (opens new window)
    • 全栈潇晨 (opens new window)
    • 博客-程序员山月-Daily (opens new window)
    • 淘系前端:冴羽 (opens new window)
  • 系列文章

    • 《图解HTTP》 (opens new window)
    • 《ES6标准入门》 (opens new window)
    • 《现代JavaScript教程》 (opens new window)
    • 《深入浅出Webpack》 (opens new window)
    • VSCode 插件系列:小茗同学 (opens new window)
    • JEST 教程 (opens new window)
    • 前端精读周刊:各种精读系列 (opens new window)
    • 一文吃透系列 (opens new window)
    • 图解 REACT 原理 (opens new window)
  • 实用网站

    • MDN (opens new window)
    • CAN I USE (opens new window)
    • TYPESCRIPT-ESLint-RULES (opens new window)
    • ESLint-RULES (opens new window)
    • FRONT-END TREND (opens new window)
    • NPM TREND (opens new window)
    • 在线分析 Node 依赖 (opens new window)
    • FIND NPM (opens new window)
    • CODE PEN (opens new window)
    • 印记中文 (opens new window)
    • TOOL.LU (opens new window)
    • 阮一峰-网道 (opens new window)
    • DIGITAL OCEAN (opens new window)
    • DEVDOCS.IO (opens new window)
    • JOI (opens new window)
  • 算法

    • 小浩算法 (opens new window)
    • LABULADONG 的算法小抄 (opens new window)
    • 力扣 SOLUTION (opens new window)
    • HACKER RANK (opens new window)
    • 代码随想录 (opens new window)
  • 博客系列

    • 美团大佬 (opens new window)
    • 蜡笔小伟 (opens new window)
    • 优秀博客1 (opens new window)
    • 优秀博客2-umi (opens new window)
    • 优质博客 (opens new window)
  • CSS

    • CSS-EASING 库 (opens new window)
    • ROUGH.JS (opens new window)
    • CSS 网站收集
    • UNOCSS (opens new window)
  • 前端

    • PROMISE (opens new window)
    • UNDERSCORE.JS (opens new window)
    • study with BGM (opens new window)
    • nginx【B站视频】 (opens new window)
    • 机器学习
    • Js基础
  • 掘金已购课程

    • 前端自动化测试精讲 (opens new window)
    • 深入浅出 Vite (opens new window)
    • 现代 Web 布局 (opens new window)
    • 前端算法与数据结构 (opens new window)
    • 基于 Vite 的 SSG 框架开发实战 (opens new window)
    • SSR 实战:官网开发指南 (opens new window)
    • WebGL 入门与实践 (opens new window)
    • 玩转 CSS 的艺术之美 (opens new window)
    • 前端调试通关秘籍 (opens new window)
    • React 进阶实践指南 (opens new window)
    • TypeScript 全面进阶指南 (opens new window)
    • 前端缓存技术与方案解析 (opens new window)
    • npm scripts 前端工作流 (opens new window)
    • Webpack5 核心原理与应用实践 (opens new window)
  • 购物车

    • 张鑫旭-技术写作指南 (opens new window)
    • 深入剖析 Node.js 底层原理 (opens new window)
    • 前端开发者的现代 C++ 课 (opens new window)
    • 从前端到全栈 (opens new window)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 编码专题

    • 00.React-目录
    • 10.React-组件-组件封装
    • 11.React-hooks-自定义钩子
    • 21.函数技巧 - 函数式编程之 compose 函数
    • 22.函数技巧-tapable钩子
    • 23.函数技巧-koa源码
    • 30.Promise系列-目录
    • 31.Promise-深入Promise原理
    • 32.Promise-指令式Promise改造
    • 32.promise-梳理 Promise 错误处理
      • 0.前言
      • 1. 场景
      • 2. 并行解决方案
      • 3.串行解决方案
        • 3.1 添加统一的错误拦截
        • 3.2 为所有异步任务添加错误拦截
      • 4. await-to-j(t)s
      • 5. 总结
    • 34.promise-竞态问题
    • 35.Promise-如何处理异步数组
  • 深入浅出 Vite

  • 快速上手 API

  • 深入浅出Babel

  • 深入浅出 React

  • 百问掘金
  • 编码专题
wangjiasheng
2023-04-27
目录

32.promise-梳理 Promise 错误处理

# 0.前言

本节博客代码仓库 (opens new window)

本篇博客用于梳理异步任务拦截错误操作,此前在网络上搜索出来的文章对异步的手法都是 try...catch 一把梭,甚至还有一篇文章《如何给所有的 async 函数添加 try/catch?》 (opens new window) 教如何使用babel 插件 + AST 手法去自动添加的操作(大佬就是 6)。

此时就产生一个疑问了,是不是所有的 async 都需要添加 try...catch 这一种写法呢?有没有其他写法呢?异步任务的错误拦截有没有更优雅的方案呢?

因此本篇博客就对这一块的知识点进行一个简单的梳理。

# 1. 场景

对于异步任务的处理,首先要区分是串行的还是并行的。

并行场景:

async function main() {
  const [resA, resB, resC] = await Promise.all([
    asyncErrA(),
    asyncErrB(),
    asyncErrC(),
  ]);
}
1
2
3
4
5
6
7

串行场景:

注:这里统一采用 async-await 语法来维护同步写法。

async function asyncErrA() {
  throw new Error("ErrorA 出错了");
}
async function asyncErrB() {
  throw new Error("ErrorB 出错了");
}
async function asyncErrC() {
  throw new Error("ErrorC 出错了");
}

async function main() {
  const resA = await asyncErrA();
  const resB = await asyncErrB();
  const resC = await asyncErrC();
}

main();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 2. 并行解决方案

对于并行场景,直接将 Promise.all 替换为 Promise.allSettled 即可。

# 3.串行解决方案

# 3.1 添加统一的错误拦截

将代码片段通过 try...catch 进行包裹

async function main() {
  ......
  try {
    const asyncFuncARes = await asyncErrA();
    const asyncFuncBRes = await asyncErrB();
    const asyncFuncCRes = await asyncErrC();
  } catch (error) {}
  ......
}
main();
1
2
3
4
5
6
7
8
9
10

【推荐】如果是一整块代码片段的话,可直接使用 .then 处理

async function main() {
  const resA = await asyncErrA();
  const resB = await asyncErrB();
  const resC = await asyncErrC();
}

main()
  .then((v) => console.log(v))
  .catch((e) => {});
1
2
3
4
5
6
7
8
9

如果报错非常规整的话,可以使用 switch...case 进行处理

async function main() {
  const asyncFuncARes = await asyncErrA();
  const asyncFuncBRes = await asyncErrB();
  const asyncFuncCRes = await asyncErrC();
}
try {
  main();
} catch (error) {
  switch (error) {
    case "[Error: ErrorA 出错了]":
      /* todo: 处理 A 出错逻辑 */
      break;
    case "[Error: ErrorB 出错了]":
      /* todo: 处理 B 出错逻辑 */
      break;
    case "[Error: ErrorC 出错了]":
      /* todo: 处理 C 出错逻辑 */
      break;
    default:
      break;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 3.2 为所有异步任务添加错误拦截

统一拦截的方案存在的问题是,当 asyncA => asyncB => asyncC 执行时,假设 asyncA 存在报错,asyncB 和 async 就不再继续执行了,如果希望做到更加细粒度的控制可以为每个异步任务进行错误拦截。

目前有两种方案可以添加拦截:

  1. 直接使用 catch
async function main() {
  const asyncFuncARes = await asyncErrA().catch((err) => {});
  const asyncFuncBRes = await asyncErrB().catch((err) => {});
  /* 如果 asyncErrC 成功返回值为 "XXX", 那 asyncFuncCRes 既有可能是 "XXX" 也有可能是 "YYY"  */
  const asyncFuncCRes = await asyncErrC().catch((err) => return "YYY");
}
main();
1
2
3
4
5
6
7

但此写法会产生一个心智负担,就是返回值 asyncFunXRes 既有可能是成功的结果,也有可能是 catch 中返回的结果。开发者在对后续结果进行处理的有可能会产生歧义。

  1. 为所有函数添加 try...catch
async function main() {
  try {
    const asyncFuncARes = await asyncErrA();
  } catch (error) {}
  try {
    const asyncFuncBRes = await asyncErrB();
  } catch (error) {}
  try {
    const asyncFuncCRes = await asyncErrC();
  } catch (error) {}
}
main();
1
2
3
4
5
6
7
8
9
10
11
12

使用 try...catch 将成功或者失败就分的很清楚,asyncFuncXRes 一定是成功的返回值。

但是此类写法的缺点就是代码结构太乱非常影响整体逻辑的阅读,下一节将使用一个第三方库来解决这个问题。

# 4. await-to-j(t)s

在 Node 的开发中的 API 设计中所有 api 的 callback 回调的第一个参数必然是 err,看一个例子:

import { access, constants } from "node:fs";

const file = "package.json";

// 检查 file 是否存在在当前的工作区中?
access(file, constants.R_OK, (err) => {
  console.log(`${file} ${err ? "is not readable" : "is readable"}`);
});
1
2
3
4
5
6
7
8

那可不可以也设计一个函数将异步 Promise 也改造成此类写法,其实社区中已存在解决方案,就是 await-to-js/ts 方案,具体的做法就是使用 to 函数将原有包裹一层:

import { to } from "await-to-ts";
async function main() {
  const [errA, asyncFuncARes] = await to(asyncErrA());
  if (errA) {
    /* 对错误进行处理*/
  }
  const [errB, asyncFuncBRes] = await to(asyncErrB());
  const [errC, asyncFuncCRes] = await to(asyncErrC());
}
main();
1
2
3
4
5
6
7
8
9
10

该库的思想非常简单,我们都知道 Promise 类在pending 状态是永远无法工作的,那该函数的目的就保证 promise 一定是存在状态不就好了。

如果自己封装的可以这么做:

function wrapAsyncFunc(asyncFunc) {
  return new Promise((resolve, reject) => {
    asyncFunc()
      .then(resolve)
      .catch(reject);
  });
}

async function main() {
  const p1 = wrapAsyncFunc(asyncErrA);
  const p2 = wrapAsyncFunc(asyncErrB);
  const p3 = wrapAsyncFunc(asyncErrC);
  const [resA, resB, resC] = await Promise.allSettled([p1, p2, p3]);
  console.log(resA.value, resB.value, resC.value);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

如果更进一步的话:

  • 如果 fillfilled 的话,返回 [null, res]
  • 如果 rejected 的话,返回 [err, undefined]
export function to<T>(promise: Promise<T>) {
  return promise
    .then<[null, T]>((data: T) => [null, data])
    .catch<[U, undefined]>((err: U) => {
      return [err, undefined];
    });
}
1
2
3
4
5
6
7

由于该库源码核心过少,在真实项目中完全可以手动实现作为工具库。

# 5. 总结

本篇主要对异步的串行和并行的拦截方案进行了一个梳理工作:

  • 对于并行写法,直接使用 Promsie.allSettled 就够用了。

  • 对于串行写法,其实也大可不必所有都添加 try...catch 。

    如果是整体代码包裹的话,直接 main.then().catch() 就挺简洁。

    如果是为每一段异步都添加错误拦截的话,到是可以偏具 Node 回调风格的 await-to-ts 库,并对该库也进行了简单实践。

编辑 (opens new window)
上次更新: 2023/10/02, 17:10:00
32.Promise-指令式Promise改造
34.promise-竞态问题

← 32.Promise-指令式Promise改造 34.promise-竞态问题→

最近更新
01
如何理解浏览器的 user agent 用户代理的含义?
11-05
02
浏览器事件循环机制
10-31
03
浏览器页面渲染机制【2023】
10-15
更多文章>
Theme by Vdoing | Copyright © 2020-2023
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式