一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第 4 天,点击查看活动详情。
分享一个 30 seconds of code 的源码解析,代码地址:pipeAsyncFunctions
原文描述
Performs left-to-right function composition for asynchronous functions.
从左往右依次执行由异步函数构成的函数组合。
Use
Array.prototype.reduce()with the spread operator(...)to perform left-to-right function composition usingPromise.then(). The functions can return a combination of: simple values,Promise's, or they can be defined asasyncones returning throughawait. All functions must be unary.
通过使用扩展运算符(...)在 Array.prototype.reduce() 里运用 Pormise.then() 从左往右执行函数组合。这些函数可以是简单的数值,Promise 对象,或定义为async 函数且通过 await 返回的异步值的混合体。且所有的函数必须是一元的(即一个自变量)。
const pipeAsyncFunctions = (...fns) =>
arg => fns.reduce((p, f) => p.then(f), Promise.resolve(arg));
// EXAMPLES
const sum = pipeAsyncFunctions(
x => x + 1,
x => new Promise(resolve => setTimeout(() => resolve(x + 2), 1000)),
x => x + 3,
async x => (await x) + 4
);
(async () => {
console.log(await sum(5)); // 15 (after one second)
})();
代码分析
知识点
箭头函数和扩展运算符就不说了,代码的核心部分是 Array.prototype.reduce() 函数。据 Mdn reduce 描述可知,该方法接收两个参数(callback,initialValue),对数组中每个元素进行操作后汇总返回一个值。
arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])
callback是累计器,接收四个参数:accumulator(累计值,上一次调用回调时返回的累积值),currentValue(当前值),index(当前值索引),array(原数组)。initialValue是初始值,作为第一次调用 callback函数时的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。 在没有初始值的空数组上调用 reduce 将报错。
总结:回调函数第一次执行时,accumulator 和 currentValue 的取值有两种情况:如果调用 reduce() 时提供了initialValue,accumulator 取值为 initialValue,currentValue 取数组中的第一个值;如果没有提供 initialValue,那么 accumulator 取数组中的第一个值,currentValue 取数组中的第二个值。
原理分析
const pipeAsyncFunctions = (...fns) =>
arg => fns.reduce((p, f) => p.then(f), Promise.resolve(arg));
pipeAsyncFunctions需定义后再使用,定义时接收函数组合,这些方法作为arguments通过...fns将类数组结构转为数组类型;arg是方法使用时接收的参数,该参数作为后面迭代器初始值使用;fns.reduce((p, f) => p.then(f), Promise.resolve(arg)),第一次执行时第一个参数p为Promise.resolve(arg),第二个参数f取函数组合的第一个值。累积器执行完成之后返回的是一个Promise对象,第二次执行时第一个参数p更新,第二个参数f取函数组合第二个值,以此类推,直到函数组合里的内容全部执行完毕;- 方法执行结束后返回的仍是一个
Promise对象。
回看 EXAMPLES 内容,最后采用立即执行函数(IIFE),封装 async 方法通过 await 执行该方法获取计算结果:
const sum = pipeAsyncFunctions(
x => x + 1,
x => new Promise(resolve => setTimeout(() => resolve(x + 2), 1000)),
x => x + 3,
async x => (await x) + 4
);
(async () => {
console.log(await sum(5)); // 15 (after one second)
})();
以上,感谢阅读。
封面图地址:Moon-man