优雅的使用Promise串行

什么是Promise串行

Promise串行是指每一个由promise封装的任务都顺序执行,即上一个执行完成后再执行下一个。

执行过程大致是下面的样子:

arduino 复制代码
Task A | ------>|
Task B |         ------>|
Task C |                 ------>|
Task D |                         ------>|

为什么要讲Promise串行

我们平时会比较多的使用并行 ,即多个任务一起执行,也就是利用Promise.all()。但其实在日常开发中串行也是会遇到的,比如依次开启走廊的所有灯,或者其次让喷泉的水枪依次喷水等等。但是ES6中的Promise并没有对串行进行直接封装,所以需要我们自己来做。

分布讲解Promise串行

Promise串行习题

之前有小伙伴发给过我一道这样的面试题,所以本文准备通过这道题来实现一下Promise串行。

定义 type Task = () => Promise (即 Task 是一个 类型,是一个返回值是 Promise 的函数类型)

假设有一个数组 tasks: Task[](每一项都是一个 Task 类型的数组)

实现一个方法 function execute(tasks: Task[]): Promise,该方法将 tasks 内的任务 依次 执行,并返回一个结果为数组的 Promise ,该数组包含任务执行结果(以执行顺序排序)

要求: 忽略异常任务,并在结果数组中用 null 占位
限制: 不添加任何依赖,仅使用 Promise,不使用 Generator 或 async

如果允许使用Generator或者async/await来写的话,会很简单,文章末尾再实现async/await的方法。

先做完成一下测试用例的代码:

javascript 复制代码
const Task = (result, isSuccess = true) => {
  return () => new Promise((resolve, reject) => {
    setTimeout(() => {
      if (isSuccess) {
        console.log(`success: ${result}`);
        resolve(result);
      } else {
        console.log(`error: ${result}`);
        reject(result);
      }
    }, 1000);
  });
}


execute([
  Task('A'),
  Task('B'),
  Task('X', false),
  Task('C'),
]).then(resultList => {
  // 这里期望打印 ["A", "B", null, "C"]
  console.log(resultList)
})

思路大致如下图: 先做一个Promise实例,然后把每个Task循环的放置到上一个promisethen回调里。

需要注意的几点:

  1. 无论每个Task是成功还是失败,它都不能阻断下一个Task的执行
  2. 最后的then需要把每个Task的执行结果"决议"出去

对策:

  1. 每一个Task外层包装一层Promise,捕获Task的reject状态
  2. 可以利用一个中间变量,缓存所有Task的输出结果,然后在最后一个Promise的then里把中间变量"决议"出去

第一版代码如下:

javascript 复制代码
function execute(tasks) {
    let resultList = [];
    return tasks.reduce(
    (previousPromise, currentPromise) => previousPromise.then((resultList) => {
       return new Promise(resolve => {
          currentPromise().then(result => {
                resultList.push(result);
             resolve()
          }).catch(() => {
                  resultList.push(null);
                 resolve(resultList.concat(null))
          })
       })
    }),
    Promise.resolve()
    ).then(() => resultList);
}

改进

其实Promise的链式操作是可以传递值的,所以可以利用这个特性,省去中间变量,

代码如下:

javascript 复制代码
function execute(tasks) {
    return tasks.reduce(
    (previousPromise, currentPromise) => previousPromise.then((resultList) => {
       return new Promise(resolve => {
          currentPromise().then(result => {
             resolve(resultList.concat(result))
          }).catch(() => {
             resolve(resultList.concat(null))
          })
       })
    }),
    Promise.resolve([])
    )
}

aysnc/await版本

代码如下:

javascript 复制代码
const execute = async (tasks = []) => {
  const resultList = [];
  for(task of tasks) {
    try {
      resultList.push(await task());
    } catch (e) {
      resultList.push(null);
    }
  }
  return resultList;
}
相关推荐
不会敲代码13 小时前
手写 Mini React:从 JSX 到虚拟 DOM 再到 render,搞懂 React 底层原理
前端·javascript·react.js
kyriewen4 小时前
你的代码仓库变成“毛线团”了?Monorepo 用 Turborepo 拆成“乐高积木”
前端·javascript·面试
身如柳絮随风扬4 小时前
你知道什么是 Ajax 吗?—— 从入门到原理,一篇彻底搞懂
前端·ajax·okhttp
旷世奇才李先生4 小时前
Vue3\+TypeScript 2026实战——企业级前端项目架构搭建与性能优化全指南
前端·架构·typescript
怕浪猫5 小时前
职场真相:稳定是陷阱,35 岁不是终点,而是重新出发的起点
面试
Beginner x_u5 小时前
前端八股整理(工程化 02)|CommonJS/ESM、Webpack Loader/Plugin 与Vite 对比
前端·webpack·node.js·plugin·loader
逻辑驱动的ken5 小时前
Java高频面试场景题25
java·开发语言·深度学习·面试·职场和发展
openKaka_5 小时前
createRoot 到底创建了什么:FiberRootNode 和 HostRootFiber 的初始化过程
前端·javascript·react.js
习明然6 小时前
UniApp开发体验感受总结
前端·uni-app
AI人工智能+电脑小能手6 小时前
【大白话说Java面试题】【Java基础篇】第32题:Java的异常处理机制是什么
java·开发语言·后端·面试