Funba
函数式编程总是令人愉悦,面向函数,相信你会快乐起来(Fun)吧(ba) -- Funba
Funba基于ramda、data.task、lodash的函数式编程应用库,核心围绕Task,通过curry&compose提供point free的代码风格,同时提供一些utils函数,Funba已通过jest覆盖单元测试
你可能需要熟悉
Usage
data.task的用例非常少,官方只展示了chain的用法,这里会先展示其他的函子接口如何使用
ap
将Task中的function apply于另一个task中的value
import ap from 'funba/ap';
import Task from 'funba/task';
import {compose} from 'funba/ramda'
import fork from 'funba/fork'
import {log, error} from 'funba/console'
const add = new Task(function (reject, resolve) {
setTimeout(function () {
resolve(function (x) {
return x + 1;
});
}, 1000);
});
const multiply = new Task(function (reject, resolve) {
setTimeout(function () {
resolve(function (x) {
return x * 10;
});
}, 5000);
});
const value = new Task(function (reject, resolve) {
setTimeout(function () {
resolve(1);
}, 1000);
});
const calculateValue = compose(fork(error, log), ap(value));
calculateValue(add); // 2
calculateValue(multiply); // 10
all
ap的高阶应用 - 并发处理相当于Promise.all
import createAction from 'funba/actionCreater';
import {compose} from 'funba/ramda'
import {error, log} from 'funba/console'
import all from 'funba/all';
const action = createAction(error);
const createTaskByNumber = n => new Task((rej, res) => {
setTimeout(() => res(n), n * 1000);
});
const [http1, http3, http5] = [1, 3, 5].map(n => createTaskByNumber(n));
const http135 = compose(action(log), all([http1, http3, http5]);
http135((x, y, z) => x + y + z); // 9
race
concat的高阶应用,处理并发竟态
const action = createAction(log);
const createTaskByNumber = n => new Task((rej, res) => {
setTimeout(() => res(n), n * 1000);
});
const [http1, http3, http5] = [1, 3, 5].map(n => createTaskByNumber(n));
compose(action(log), race)([http1, http3, http5]); // log 1
bimap
分别处理一个函子中的reject和resolve
import Task from 'funba/Task';
import { compose } from 'funba/ramda';
import bimap from 'funba/bimap';
import fork from 'funba/fork';
import {error, log} from 'funba/console'
const cfTask = n => new Task((rej, res) => {
if (n >= 5) {
res(n)
} else {
rej(n)
}
});
compose(fork(error, log), bimap(x => x + 1, x => x - 1), cfTask)(5); // log - 4
compose(fork(error, log), bimap(x => x + 1, x => x - 1), cfTask)(2); // error - 3
chain
串联执行task
const cfTask = n => new Task((rej, res) => {
res(n);
});
const add = x => new Task((rej, res) => {
res(x + 5);
});
const mutiply = x => new Task((rej, res) => {
res(x * 5);
});
compose(fork(error, log),chain(mutiply), chain(add), cfTask)(5); // 50
fold
分别作用于函子的reject和resolve,并在外部的resolve获取
bimap的不同点在于 bimap可以在reject中获取f作用的value
const cfTask = n => new Task((rej, res) => {
if (n >= 5) {
res(n)
} else {
rej(n)
}
});
compose(fork(error, log), fold(x => x + 1, x => x - 1), cfTask)(5); // log 4
compose(fork(error, log), fold(x => x + 1, x => x - 1), cfTask)(2); // log 3
fork
Task的执行api
import fork from 'funba/fork';
import Task from 'funba/Task';
import {error} from 'funba/console';
new Task((rej) => rej(1)).fork(error); // error 1;
id
将参数直接返回
import id from 'funba/id';
id(2) === 2;
id(true) === true
of
Task生成器
import of from 'funba/of'
of(1) === new Task((rej, res) => res(1));
rejectedMap
rejectedMap反向的map
new Task((rej) => rej(1)).rejectedMap(x => x + 5).fork(error); // error 6
orElse
反向的chain,作用在task的reject
const cfTask = n => new Task((rej, res) => {
if (n >= 5) {
res(n)
} else {
rej(n)
}
});
const add = n => new Task((rej, res) => {
rej(n + 5);
})
const multiply = n => new Task((rej, res) => {
rej(n * 5);
})
const multiply2 = n => new Task((rej, res) => {
res(n * 8);
})
compose(fork(error), orElse(multiply), orElse(add), cfTask)(1); // error 30
createAction
快速生成一个action - 封装fork(f, g)行为
import createAction from 'funba/createAction';
import {compose} from 'funba/ramda'
import {error, log} from 'funba/console'
const action = createAction(error);
const throwErr = n => new task((rej) => rej(n));
const run = compose(action(log), throwErr);
run(5); // error 5
createApi
api生成器
import api from 'funba/createApi';
import axiosHttp from 'funba/axiosHttp';
const axiosApi = api(axiosHttp);
const mockData = {'/api/login': {name: 'lemon'}};
const baiduApi = axiosApi('https://www.baidu.com' , mockData);
// 开启mock之后GetHttp&PostHttp都会返回mock数据
// Mock始终返回mock数据
const {
GetHttp,
PostHttp,
Mock
} = baiduApi(true);
// 同一个项目有非常多的api host的话
const baiduApi = axiosApi('https://www.baidu.com' , mockData);
const wxApi = axiosApi('https://www.wx.com' , mockData);
const alibabaApi = axiosApi('https://www.alibaba.com' , mockData);
bringFunction
类似于curry 将一个函数携带值去应用
import bringFunction from "funba/bringFunction";
const add = x => x + 1;
const _add = bringFunction(add);
const _add1 = _add(1);
_add1(); // 2
applyFunction
将参数传入一组函数使用,参数依次进行传递
import applyFunction from 'funba/applyFunction'
const data = {
count: 0
};
const add1 = data => data.count+=1;
const add3 = data => data.count+=3;
const add5 = data => data.count+=5;
const app = applyFunction([add1, add3, add5]);
app(data); // data.count === 9
axiosApi
提供一个封装了axios的api
import axiosApi from 'funba/axiosApi'
const baiduApi = axiosApi('https://www.baidu.com', {});
const {
GetHttp,
PostHttp,
Mock
} = baiduApi(true); // 开启mock
axiosHttp
提供了一个封装了axios的task,也可以直接使用axiosApi
import axiosHttp from 'funba/axiosHttp';
import fork from 'funba/fork';
import {log, error} from 'funba/console';
const sayHi = compose(fork(error, log), axiosHttp);
sayHi('www.baidu.com', 'get', '/api/sayHi', 'lemon');
backArgs
运行一个函数,但是返回参数
import backArgs from 'funba/backArgs';
const a = {
count: 1
};
const b = {
count: 2
}
const add = backArgs(x => b.count = b.count + x.count);
add(a) === a // true
checkSpace
检查空字符
import checkSpace from 'funba/checkSpace';
checkSpace('') // true
createParamsString
将一个对象转换成query string
import createParamsString from 'funba/createParamsString';
createParamsString({a: 1, b: 2}) === '?a=1&b=2';
date
提供轻量级的时间格式化工具
import {dateFormat_yy_mm_dd} from 'funba/date';
dateFormat_yy_mm_dd(Date.now()); // 2021-12-01;
invertBoolean
转换成反布尔值
import invertBoolean from 'funba/invertBoolean';
invertBoolean(1) === false
mock
接收一个mockdata对象 返回一个mock请求Task
// createApi源码部分
const mockHttpApi = mock(mockData); // 直接返回一个mock
const curryMockHttpApi = curry(mockHttpApi);
const getMockHttp = curryMockHttpApi(server, 'GET');
const Mock = (url: string) => compose(getMockHttp(url), id); // mock请求
createMockData
收集mockData
const loginMock = {
'/api/login': {name: 'lemon'}
}
const articleMock = {
'/api/getList': []
};
const mockData = createMockData(); // createMockData可以接收一个格式化函数
const allMockData = mockData(
loginMock,
articleMock
);
packPromise
接收一个Promise,让这个promise始终有返回值
const a = async () => packPromise(new Promise((res, rej) => {
setTimeout(() => res(5), 2000);
}));
const [res, err] = await a(); // res 5 , err undefined
prop
获取属性
import prop from 'funba/prop';
prop('key', {key: 1}); // 1
compose
函数组合, 来自ramda
import {compose} from 'funba/ramda'
curry
函数柯里化, 来自ramda
import {curry} from 'funba/ramda'
map
map方法,既可以处理函子也可以处理数组, 来自ramda
import {map} from 'funba/ramda'
concat
concat, 来自ramda
import {concat} from 'funba/ramda'
head
head,取出数组第一项, 来自ramda
import {head} from 'funba/ramda'
selectNum
一定范围内的随机数
import selectNum from 'funba/selectNum';
selectNum(0, 10); // 5
replace
柯里化字符串替换
import replace from 'funba/replace';
const replaceSpace = replace(regSpace, '');
replaceSpace('12 43 45'); // 124345
requestLog
log请求日志
trace
compose中的断点工具
compose(log, trace('after error'), error)
localStorage
操作localStorage
const saveStorage = setLocalStorage('user');
saveStorage({name: 'lemon'});
getLocalStorage('user'); => {name: 'lemon'}
isWx
判断是否为微信内
Test
yarn test
yarn report