## 1 引言

本周精读的文章是 [why-using-reduce-to-sequentially-resolve-promises-works](https://css-tricks.com/why-using-reduce-to-sequentially-resolve-promises-works/)，讲了如何利用 reduce 实现 Promise 串行执行。

在 async/await 以前 Promise 串行执行还是比较麻烦的，希望根据这篇文章可以理清楚串行 Promise 的思维脉络。

## 2 概述

除了依赖 [async](https://github.com/caolan/async) [promise-fun](https://github.com/sindresorhus/promise-fun) 等工具库，最常用的队列操作就是 `Array.prototype.reduce()` 了：

```javascript
let result = [1, 2, 5].reduce((accumulator, item) => {
  return accumulator + item;
}, 0); // <-- Our initial value.

console.log(result); // 8
```

最后一个值 0 是起始值，每次 reduce 返回的值都会作为下次 reduce 回调函数的第一个参数，直到队列循环完毕，因此可以进行累加计算。

那么将 `reduce` 的特性用在 Promise 试试：

```javascript
function runPromiseByQueue(myPromises) {
  myPromises.reduce(
    (previousPromise, nextPromise) => previousPromise.then(() => nextPromise()),
    Promise.resolve()
  );
}
```

当上一个 Promise 开始执行（`previousPromise.then`），当其执行完毕后再调用下一个 Promise，并作为一个新 Promise 返回，下次迭代就会继续这个循环。

```javascript
const createPromise = (time, id) => () =>
  new Promise(solve =>
    setTimeout(() => {
      console.log("promise", id);
      solve();
    }, time)
  );

runPromiseByQueue([
  createPromise(3000, 1),
  createPromise(2000, 2),
  createPromise(1000, 3)
]);
```

得到的输出是：

```plain
promise 1
promise 2
promise 3
```

## 3 精读

`Reduce` 是同步执行的，在一个事件循环就会完成（更多请参考 [精读《Javascript 事件循环与异步》](https://github.com/dt-fe/weekly/blob/master/30.%E7%B2%BE%E8%AF%BB%E3%80%8AJavascript%20%E4%BA%8B%E4%BB%B6%E5%BE%AA%E7%8E%AF%E4%B8%8E%E5%BC%82%E6%AD%A5%E3%80%8B.md)），但这仅仅是在内存快速构造了 Promise 执行队列，展开如下：

```javascript
new Promise((resolve, reject) => {
  // Promise #1

  resolve();
})
  .then(result => {
    // Promise #2

    return result;
  })
  .then(result => {
    // Promise #3

    return result;
  }); // ... and so on!
```

`Reduce` 的作用就是在内存中生成这个队列，而不需要把这个冗余的队列写在代码里！

### 更简单的方法

感谢 [eos3tion](https://github.com/eos3tion) 同学补充，在 async/await 的支持下，`runPromiseByQueue` 函数可以更为简化：

```javascript
async function runPromiseByQueue(myPromises) {
  for (let value of myPromises) {
    await value();
  }
}
```

多亏了 async/await，代码看起来如此简洁明了。

不过要注意，这个思路与 `reduce` 思路不同之处在于，利用 `reduce` 的函数整体是个同步函数，自己先执行完毕构造 Promise 队列，然后在内存异步执行；而利用 async/await 的函数是利用将自己改造为一个异步函数，等待每一个 Promise 执行完毕。

### 更多 Promise 拓展

[天猪](https://github.com/atian25) 同学分享的 [promise-fun](https://github.com/sindresorhus/promise-fun) 除了串行 Promise 解决方案，还提供了一系列 Promise 功能拓展（有一些逐渐被 ES 标准采纳，比如 [finally](https://github.com/tc39/proposal-promise-finally) 已经进入 Stage 4），如果你的项目还无法使用 async/await，是不需要自己重新写一遍的，当然本文的原理还是需要好好理解。

> Stage 相关可以进行拓展阅读 [精读《TC39 与 ECMAScript 提案》](https://github.com/dt-fe/weekly/blob/master/15.%E7%B2%BE%E8%AF%BB%20TC39%20%E4%B8%8E%20ECMAScript%20%E6%8F%90%E6%A1%88.md)。

## 4 总结

Promise 串行队列一般情况下用的不多，因为串行会阻塞，而用户交互往往是并行的。那么对于并行发请求，前端按串行顺序接收 Response 也是一个有意思的话题，留给大家思考。

## 5 更多讨论

> 讨论地址是：[精读《用 Reduce 实现 Promise 串行执行》 · Issue #109 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/109)

**如果你想参与讨论，请[点击这里](https://github.com/dt-fe/weekly)，每周都有新的主题，周末或周一发布。前端精读 - 帮你筛选靠谱的内容。**
