# 深度剖析 redux applyMiddleware 中 compose 构建异步数据流的思路

### 正文

1. 获取code；
2. 获取openId、AccessToken；
3. 根据openId、获取openId、AccessToken；获取用户信息实现自动注册然后登录；
4. 跳到商品购买页。

``````const fucArr = [
next=>{
setTimeout(()=>{
console.log(1);
next()
}, 300)
},
next=>{
setTimeout(()=>{
console.log(2);
next()
}, 200)
},
next=>{
setTimeout(()=>{
console.log(3);
next()
}, 100)
}
]``````

``    fucArr( fucArr );// funcArr 运行报错 TypeError: next is not a function``

``    fucArr( ()=>fucArr(()=>{}) ); //1 2 ``
``两个函数顺序执行搞定了那三个函数岂不是，没错,小case。``
``    fucArr( ()=>fucArr(()=>{ fucArr(()=>{}) }) );// 1 2 3``

OK开始，既然是循环那就来个for循环吧，既然是下一个函数传给上一个当参数，得让相邻的两个函数出现在同一个循环里啦。于是有了起手式：

``````    for (let index = 0; index < fucArr.length; index++) {
const current = array[index];
const next = array[index + 1];
current(()=>next())
}``````

``````    a(()=>{
b(c)
})``````

``````    a(c=>{
b(c)
})
// 我们再往下写一层
a(
d=>{
(
c=>b(c)
)(
d=>c(d)
)// 为了避免你们看不懂我在写啥，我告诉你你，这玩意儿是函数自调用
}
)
// 怎么样是不是有一种豁然开朗的赶脚``````
• 我们发现每次新加入一个函数，都是重新构建一次`a`函数里的参数，以下我将这个参数简称函数`d`
• 于是乎我们来通过循环构建这个`d`
• 为了让循环体都能拿到`d`，因此它肯定是在循环的上层作用域
• 而且`d`具有两个特性：
1. 能接受一个函数作为参数，这个函数还能接收另一个函数作为参数，并会在某个时刻进行调用
1. 每次循环都会根据当前`d`，然后加入当前函数，按照相同模式进行重构；
• ps: 我们发现这两个特性其实和咱们传入的每个函数特性是一致的。
``   于是乎咱们把第一个数组的函数组作为起始函数：``
``````    var statusRecord = fucArr;
for (let index = 1; index < fucArr.length; index++) {
statusRecord = next=>statusRecord(()=>fucArr[index](next))
}``````
``````  写完发现这样是错误的，如果调用函数statusRecord那就会变成，自己调自己，自己调自己，自己调自己，自己调自己~~皮一下很开心~~...的无限递归。
``````
statusRecord = ((statusRecord)=>(next)=>statusRecord(()=>fucArr[index](next))(statusRecord))``````
``````  为什么index不传呢？因为index是let定义，可以看做块级作用域，又有人要说js没有块级作用域，我：你说得对，再见。
最后咱们得到的还是这个模型要调用，别忘了传入一个函数功最后数组最后一个函数调用。不然会报错``````
``    statusRecord(()=>{}) // 输出1、2、3``
``那咱们的功能就此实现了；不过可以优化一哈。咱们上面的代码有几个要素:``
1. 数组循环
2. 状态传递
3. 初始状态为数组的第一个元素
4. 最终需要拿到单一的返回值
``不就是活脱脱用来描述reduce的吗？于是乎我们可以这样撸``
``````    //pre 前一个状态、 cur当前循环函数、next 待接收的下一个
fucArr.reduce((pre, cur)=>{
return (next)=>pre(()=>cur(next))
})(()=>{})// 1 2 3``````
``   以上异步顺序调用的问题咱们已经理解了，咱们依次输出了1，2，3。但是咱们现实业务中常常是下一个函数执行，和上一个函数执行结果是关联的。咱们就想能不能改动题目贴合实际场景，上一个函数告诉下一个函数`console.log(n)`，于是乎题目做了一个小调整。``
``````    const fucArr = [
next=>{
setTimeout(()=>{
console.log(1);
next(2)
}, 300)
},
// 函数2
(next,n)=>{
console.log(n);
next(3)
},
// 函数3
(next,n)=>{
console.log(n);
next(4)
}
]

fucArr.reduce((pre,cur)=>{
return (next)=>pre((n)=>cur(next,n))
})((n)=>{console.log(n)})// 1 2 3 4``````
``   哇，功能又实现了，我们真棒。现在我们来回忆一下redux里中间件里传入函数格式``
``````store=>next=>action=>{
// dosomething...
next()
}``````
``    在某一步中store会被剥掉，在这就不细说了，于是咱们题目再变个种``
``````    const fucArr = [
next=>n=>{
setTimeout(()=>{
console.log(n);
next(n+1)
}, 300)
},
// 函数2
next=>n=>{
setTimeout(()=>{
console.log(n);
next(n+1)
}, 300)
},
// 函数3
next=>n=>{
setTimeout(()=>{
console.log(n);
next(n+1)
}, 300)
}
]``````

``````    fucArr.reduce((pre,cur)=>{
return (next)=>pre(cur(next))
})((n)=>{console.log(n)})``````

``````    // before 之前的第一个函数和函数模型
next=>{
setTimeout(()=>{
console.log(1);
next(n+1)
}, 300)
}
a(c=>{
b(c)
})

// ------------
// after 现在的第一个函数和函数模型
next=>n=>{
setTimeout(()=>{
console.log(n);
next(n+1)
}, 300)
}
a(b(c))
// 发现现在的第一个函数调用之后，一个函数。这个函数还要再接收一个参数去启动``````

(⊙v⊙)嗯没错，经过精妙的分析我知道要怎么做了。

``````    fucArr.reduce((pre,cur)=>{
return (next)=>pre(cur(next))
})((n)=>{console.log(n)})(1)// 1 2 3 4``````

``````    const compose = fucArr=>{
if(fucArr.length === 0) return;
if(fucArr.length === 1)    return fucArr((n)=>{console.log(n)})(1)
fucArr.reduce((pre,cur)=>{
return (next)=>pre(cur(next))
})((n)=>{console.log(n)})(1)
}``````

1. length === 0 时： 返回一个传入什么返回什么的函数。
2. length === 1 时： 直接返回传入函数函数。
3. length > 1 时： 构建一个a(b(c(....)))这种函数调用模型并返回，使用者自定义最后一环需要运行的函数，并且能够定义进入第一环的初始参数
``````    // 原版
function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}

if (funcs.length === 1) {
return funcs
}

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

### 结语

• bind
• 递归调用
• 通过new Promise 函数，将resolve作为参数方法传入上一个函数然后改变Promise状态...，
• 如果大家有兴趣可以自己实现一下，为了不把大家的思路带歪，在写的过程中并没有体现出来。

731 声望
4 粉丝
0 条评论
{"ft_lang_label":"__label__zh","ft_lang_prob":0.808021,"math_prob":0.99851793,"size":4942,"snap":"2022-27-2022-33","text_gpt3_token_len":3159,"char_repetition_ratio":0.145403,"word_repetition_ratio":0.058536585,"special_character_ratio":0.3170781,"punctuation_ratio":0.1822034,"nsfw_num_words":1,"has_unicode_error":false,"math_prob_llama3":0.9787961,"pos_list":[0,1,2],"im_url_duplicate_count":[null,null,null],"WARC_HEADER":"{\"WARC-Type\":\"response\",\"WARC-Date\":\"2022-07-06T16:00:26Z\",\"WARC-Record-ID\":\"<urn:uuid:72ee0f38-0b23-4278-8c23-0707e74b6b97>\",\"Content-Length\":\"48961\",\"Content-Type\":\"application/http; msgtype=response\",\"WARC-Warcinfo-ID\":\"<urn:uuid:9541b2f9-5c87-4d60-9374-9282000e9f8f>\",\"WARC-Concurrent-To\":\"<urn:uuid:97a11c83-1e5f-403b-8183-3ac8fce14079>\",\"WARC-IP-Address\":\"103.61.39.86\",\"WARC-Target-URI\":\"https://segmentfault.com/a/1190000017480726\",\"WARC-Payload-Digest\":\"sha1:UIXACLL4KNTIY55PI673MIABA2A7OA6W\",\"WARC-Block-Digest\":\"sha1:QEM44WQHGUWI6F4OTREUSOKO7YPJSLPC\",\"WARC-Identified-Payload-Type\":\"text/html\",\"warc_filename\":\"/cc_download/warc_2022/CC-MAIN-2022-27/CC-MAIN-2022-27_segments_1656104675818.94_warc_CC-MAIN-20220706151618-20220706181618-00503.warc.gz\"}"}