> For the complete documentation index, see [llms.txt](https://yes-1-am.gitbook.io/blog/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://yes-1-am.gitbook.io/blog/javascript/xie-gei-zi-ji-kan-de-next-han-shu.md).

# 写给自己看的 next 函数

## 前言

最早在看 `redux` 源码的时候，有个 applyMiddleware 函数，没能看明白(现在连它能干嘛的都忘了，衰)。只隐约记得，能够把一些 middleware 传入其中，然后它依次处理，顺序的执行一些逻辑(真忘了是干嘛的了)。

再如 express 中，某个中间件的使用都是 `app.use(middleware)`, 而其内部具体干了什么还是不太清楚，最终呈现的效果是，当请求来的时候，依次的执行中间件，直到最后返回。

那这两个知识盲点给我的感觉是，都有种 "**某个数组里存着函数,而里面的函数会依次执行**" 的感觉。之后，偶然看到这个[分享](https://www.yuque.com/online-sharing/activity/fm8or7)，说实话，觉得是能解决我的疑惑的，但还是绕不过来，理不清楚。

这次抽空强行理解了一下，赶紧留住思路，免得跑了\~

### 需求

想象我们有这样的一个数组:

```javascript
const fun1 = (next) => {
  setTimeout(() => {
    console.log(1);
    next();
  }, 3000);
}

const fun2 = (next) => {
  setTimeout(() => {
    console.log(2);
    next();
  }, 2000);
}

const fun3 = (next) => {
  setTimeout(() => {
    console.log(3);
    next();
  }, 1000);
}

const funArr = [
  fun1,
  fun2,
  fun3
];


// 实现 run 方法，让数组中的函数依次执行  
function run() {

}
```

## 1. 自己的理解过程

### 1.1 正确分析

关键是 next 这个参数，我们需要的就是在 next 执行的时候，能够使得下一个函数执行，那么假如下一个函数就是 next, 会怎么样？

先只有两个函数:

`fun1(fun2)`

当 fun1 执行完之后，fun2 作为 next 执行，看起来没有问题。

但是！，当 fun2 执行的时候，fun2 的参数是 undefined，也就是说 fun2 的next 是 undefined ，那执行到 fun2 中的 next() 的时候就会报错。

那我们就想，给 fun2 传一个空函数:

```javascript
fun1(fun2(() => {}))
```

这样也会有问题，就是 `fun2(() => {})` 这是一个函数调用，当它作为参数的时候，首先会被计算出来。同时 `fun2(() => {})`的返回值是 undefined, 又相当于 fun1 的参数是 undefined, 又会报错。

那么我们需要的就是，fun2 自身接受一个空函数，但是又不可以直接以 fun2() 的形式传给 fun1 ，那么我们考虑把 fun2 包裹在函数里:

```javascript
fun1(() => fun2(() => {}))
```

那这样就顺利的实现了功能。

那如果有三个函数的时候，我们自然可以推出以下的式子:

```javascript
fun1(() => fun2(() => fun3(() => {})))
```

是的，但是，当函数个数不一定时，我们怎么来凑出这个式子呢？

先直接上 reduce 吧(就是这么没有道理):

先看看 reduce 的结构:

```javascript
arr.reduce((prev, curr) => {
    // code
}, initValue)
```

prev 为 initValue, 之后是 `code` 的返回值. curr 为当前迭代到的元素:

回到我们的例子：\
当一个函数时:

```javascript
fun1(() => {})
```

当两个函数时:

```javascript
fun1(() => fun2(() => {}))
```

当三个函数时:

```javascript
fun1(() => fun2(() => fun3(() => {})))
```

套用 reduce， 那就是:

```javascript
function run(arr) {
    return arr.reduce((prev,curr) => {
        // 我们知道 curr 会依次为，fun1, fun2, fun3
        // 那 curr 为 fun1 时，我们为了凑出 fun1(() => {})
        // 因此让 prev 为 () => {}
        return () => curr(prev)
    }, () => {})
}

// 最终调用
run(funArr)()  // 因为 run 函数会返回一个函数
```

检验一下其余的:\
curr 为 fun1 时，run 函数最终返回:

```javascript
() => fun1(() => {})     // prev
```

curr 为 fun2 时，prev 为 `() => fun1(() => {})`, run 函数最终返回:

```javascript
() => fun2(() => fun1(() => {}))   // prev
```

curr 为 fun3 时，prev 为 `() => fun2(() => fun1(() => {}))`, run 函数最终返回:

```javascript
() => fun3(() => fun2(() => fun1(() => {})))
```

好像像那么回事，但是顺序好像反了，会先执行 fun3 而不是 fun1, 那我们换成 **reduceRight**:

```javascript
function run(arr) {
    return arr.reduceRight((prev,curr) => {
        return () => curr(prev)
    }, () => {})
}
// 最终调用
run(funArr)()  // 因为 run 函数会返回一个函数
```

那结果应该是:

```javascript
() => fun1(() => fun2(() => fun3(() => {})))
```

**最终实现了需求**

### 1.2 易错

在理解 1.1 上述过程中，我尝试了以下的代码:

```javascript
function run(arr) {
    arr.reduce((prev,curr) => {
        return curr(prev)
    }, () => {})
}
```

以上代码(***在runJS***)会输出 3,2,1 于是，我以为我改成 reduceRight 就可以了，但是改成 reduceRight 之后，依旧是输出 3,2,1. 那是为什么呢？

先转换以上代码:

curr 为 fun1 时，prev 为 `() => {}`, 此时结果为:

```javascript
fun1(() => {})   // prev
```

curr 为 fun2 时，prev 为 `fun1(() => {})`, 此时结果为:

```javascript
fun2(fun1(() => {}))   // prev
```

curr 为 fun3 时，prev 为 `fun2(fun1(() => {}))`, 此时结果为:

```javascript
fun3(fun2(fun1(() => {})))
```

**注意:**

以上代码其实是会**报错**的！！！，如一开始分析的那样， fun1 会首先执行，返回值为 undefined, 当它作为 fun2 的参数时，其实是**报错了**，但是 runJS 居然没有报错。

> 题外话，之后找到了 runjs 的 github，发现居然不开源，也发现一个有意思的讨论: [issue](https://github.com/lukehaas/RunJS/issues/30)

以上的代码为什么还能输出结果呢？

因为在不报错的情况下，以上代码相当于:

```javascript
fun1()
fun2()
fun3()
```

即相当于三个函数同时执行，因此最终的输出结果，**在 setTimtout 的时间不同的情况下，只和时间有关**。

***因此也告诉我们，我们的这种场景, reduce 里面必须要返回函数***

## 2. 更多方法

[参考](https://www.yuque.com/online-sharing/activity/fm8or7)

### 2.1 递归法

```javascript
function run(arr) {
  if(arr.length === 0) {
    return;
  }
  arr[0](() => run(arr.slice(1)))
}
```

其中 next 是个函数，相当于 `() => run(arr.slice(1))`， 每次 next 函数执行，都会执行数组的下一项。

### 2.2 方法二

```javascript
function run(arr){
  const trigger = () => {
    if (arr.length === 0) {
      return;
    }
    arr.shift()();
  };
  arr = arr.map(fun => () => fun(trigger));
  trigger();
};
```

即 next 的功能就是执行数组的下一项，因此我们先定义 **trigger** 作为 next. 然后我们通过 map 函数，给每个函数外面包一层，当函数执行的时候，主动给它们传入 **trigger** 这个函数.

### 2.3 Promise 的方法

```javascript
function run(arr) {
  const trigger = () => {
    if (arr.length === 0) {
      return;
    }
    arr.shift()();
  };
  arr = arr.map(fun => () => new Promise((resolve) => {
    fun(resolve);
  }).then(trigger));
  trigger();
};
```

想一下，next 和 Promise 中的 resolve 是不是有点类似，就都是当执行的时候，会触发后续代码的执行。

于是我们同样构造 **trigger** 函数，但这次，我们利用 Promise#resolve 之后，执行 .then 代码的特性，来触发 **trigger**\
\&#xNAN;*个人觉得这种方法和 2.2 其实是一样的，而且添加了 Promise, 增加了执行流程，不推荐*

## 3. compose 函数

函数式编程中有个 compose 函数，这个函数组合几个函数，形成一个新的函数:

```
const compose = ([f1, f2,f3]) => value => f1(f2(f3(value)));
```

即 compose 返回一个新的函数，该函数会被传入的函数,从右到左依次执行，同时右边函数的结果会作为左边函数的参数。

```
function compose(funcs) {
  if (funcs.length === 0) {
    return arg => arg;
  }

  if (funcs.length === 1) {
    return funcs[0];
  }

  return funcs.reduce((a, b) => (...args) => a(b(...args)));
}
```

我们来看 reduce 的部分, 拆开是这样的:

```javascript
funcs.reduce((prev,curr) => {
    return (...args) => {
        prev(curr(...args))
    }
})
```

*当 reduce 没有第二个参数 initValue 时，prev 即为 funcs\[0], curr 即为 funcs\[1]。*

因此，当只有两个函数时, prev 为 `fun1`, curr 为 `fun2`, 最终返回的函数是:

```javascript
(...args) => {              // prev
    fun1(fun2(...args))
}
```

当有三个函数时:\
prev 为:

```javascript
(...args) => {
    fun1(fun2(...args))
}
```

curr 为 `fun3`, 那么最终的结果就是:

```javascript
(...args) => {
    (
        (...innerArgs) => {
            fun1(fun2(...innerArgs))
        }
    )
    (fun3(...args))
}

// 注意
以下是一个函数声明:
(
    (...innerArgs) => {
        fun1(fun2(...innerArgs))
    }
)

而 fun3(...args) 是上面函数的参数，因此最终替换过来就相当于:  

(...args) => {
    fun1(fun2(fun3(...args)))
}
```

### 3.1 示例

```javascript
function add1(number) {
    return number + 1
}

function multiply2(number) {
    return number * 2
}

function minus3(number) {
    return number - 3
}

const newFun = compose([add1,multiply2,minus3])
newFun(6)   // 输出 7 
// 7 = ((6 - 3) * 2) + 1
```

## 参考资料

1. 语雀: [来聊一道面试题吧](https://www.yuque.com/online-sharing/activity/fm8or7)
2. 知乎: [Node.js 的中间件里面的 next() 是干什么的？](https://www.zhihu.com/question/37693420/answer/176287446)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://yes-1-am.gitbook.io/blog/javascript/xie-gei-zi-ji-kan-de-next-han-shu.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
