常用的函数工具函数

函数式是一种很好用的编程方式,使用函数式编程一开始可能还不是很习惯,但在你习惯之后会发现这能帮你编写出简洁且充满表达能力的代码。下面介绍一下,日常开发中运用函数式的一些技巧以及函数式的原理。

compose

1
compose(...fns)

compose基本是大部分函数式都会用到的函数,他的作用是把传入的方法从右往左,把前一个的返回值返回给下一个,redux的中间件就是基于compose实现的。

源码

compose 的源码也比较简单,但是思维方式可能会比较绕。简单来说就是通过reduce让函数一层一层嵌套,传进的函数会嵌套在最里面,所以会最先执行。

1
2
3
4
5
6

function compose(...fns) {
if (fns.length === 0) return args => args;
if (fns.length === 1) return fns[0];
return fns.reduce((f,g) => (...args) => f(g(...args)))
}

常见的用法,除了有reduce这种中间件的用法外,还可以是比方说我们有几个步骤,每个步骤需要把上一个步骤的值传进来,我们就可以利用compose来简化我们的写法。

1
2
3
4
5
6

compose(
step3,
step2,
step1,
)('begin')

cancellable

开发中可能会碰到这样的需求,需要让一些函数变成可取消的。这部分的逻辑其实是可以复用的。下面看看可以如何去实现这样一个功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

// 首先我们可以实现一个类似axios里的CancelToken
function createCancelToken() {
let cancel;
const promise = new Promise((_, reject) => {
cancel = reject;
});
return {
promise,
cancel
}
}

function cancellable(fn, cancelPromise) {
return (...args) => {
return Promise.race([fn.apply(null, ...args), cancelPromise])
}
}

async function request() {}
const cancelToken = createCancelToken();
const cancellableRequest = cancellable(request, cancelToken.promise);

cancellableRequest();
// 调用这个函数即可取消上面的cancellableRequest
cancelToken.cancel();

concurrent

用来控制函数并发量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function concurrent(count, fn) {
const semaphore = new Semaphore(count);

return async function (...args) {
try {
await semaphore.acquire();
return await fn(...args);
} finally {
semaphore.release();
}
}
}

async function request() {}
// 并发量只有1的
const concurrentRequest = concurrent(1, request);
concurrentRequest().then(() => {})
// 只有等上一个结束了这个函数才会执行
concurrentRequest().then(() => {})

retryable

碰到错误就重试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23


function retryable(fn, times) {
return async (...args) => {
let retryTimes = 0;
while (true) {
try {
const res = await fn(...args);
return res;
} catch (e) {
retryTimes++;
if (retryTimes >= times) {
throw e;
}
}
}
}
}

async function request(){}
const retryableRequest = retryable(request, 5);
// 重试5次后任然失败则会抛出错误
retryableRequest()

结合

稍微改造一下,我们就可以通过compose来结合上面几个函数最终生成一个功能强大的request

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
const cancellable = (cancelPromise) => (fn) => {
return (...args) => {
return Promise.race([fn.apply(null, ...args), cancelPromise])
}
}

const concurrent = (count) => (fn) => {
const semaphore = new Semaphore(count);

return async function (...args) {
try {
await semaphore.acquire();
return await fn(...args);
} finally {
semaphore.release();
}
}
}

const retryable = (times) => (fn) => {
return async (...args) => {
let retryTimes = 0;
while (true) {
try {
const res = await fn(...args);
return res;
} catch (e) {
retryTimes++;
if (retryTimes >= times) {
throw e;
}
}
}
}
}
const cancelToken = createCancelToken();

// 这样我们就可以得到一个同时拥有这三个能力的request函数了
const strongRequest = compose(
cancellable(cancelToken.promise),
concurrent(2),
retryable(5),
)(request)