funba

function library

Usage no npm install needed!

<script type="module">
  import funba from 'https://cdn.skypack.dev/funba';
</script>

README

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

actionCreater

快速生成一个action - 封装fork(f, g)行为

import createAction from 'funba/actionCreater';
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

apiCreater

api生成器

import api from 'funba/apiCreater';
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);

apply

类似于curry 将一个函数携带值去应用

import apply from "funba/apply";
const add = x => x + 1;
const _add = apply(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请求

packPromise

接收一个Promise,让这个promise始终有返回值

const a = async () => packPromise(new Promise((res, rej) => {

    setTimeout(() => res(5), 2000);
}));
const [res, err] = await a(); // res 5 , err undefined

pagination

Task翻页器

import usePagination from 'funba/pagination';
const axiosList = params => new Task((rej, res) => {
    setTimeout(() => {
        res({
            content: [1,2,3],
            total: 20
        });
    })
})
// 接收一个http task
// const { initPage = 1, initSize = 10, params = {} } = initConfig || {}; 默认参数
const {list, getList, use, ifDone, initList} = usePagination(axiosList);
list // 所有的请求数据
getList // 每次都会执行翻页请求
use // 注入中间件
ifDone // 是否翻页完成
initList // 将会重新开始请求

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请求日志

toast

web端的toast工具

trace

compose中的断点工具

compose(log, trace('after error'), error)

useCallback

react hook - useCallback

import {rememberFunction} from 'funba/useCallback';
rememberFunction === useCallback(f, []);

useEffect

react hook - useEffect

import {afterFinishRender} from 'funba/useEffect';
afterFinishRender === useEffect(f, []);

useRefCallback

react hook - useRef+useCallback

export default function useRefCallback<T extends (...args: any[]) => any>(callback: T) {
    const callbackRef = useRef(callback);
    callbackRef.current = callback;
    return useCallback((...args: any[]) => callbackRef.current(...args), []) as T;
  }

Test

yarn test
yarn report

report

markdown