函数式编程在实际项目中的运用(异步)

1,118 阅读2分钟

实现一个小小的需求

  • 弹窗在打开的时候请求图书信息
  • 把接口返回的图书信息填充到表单 image.png

代码实现

/**
 * @function
 * - 弹窗首次加载
 * - 表单用的是TableRender
 */
async function formOnMount() {
  const bookInfo = await getCurrentBookInfo();
  form.setValues(bookInfo);
}

/**
 * @function
 * - 获取当前点击的图书的信息
 * - dispatch支持的范型可以看我那一篇愉快的使用Dva那一篇文章,或者私聊我
 */
function getCurrentBookInfo(): Promise<Record<string, string>> {
  return dispatch<BookManagerModel, Promise<Record<string, string>>>({
    type: 'bookManagerModel/getCurrentBookInfo'
  });
}

// 这里是写在Dva里Effect的请求方法
* getCurrentBookInfo(_, {call, select}) {
  const id = yield select((state: AllModels) => state.bookManagerModel.currentBookId);
  const temp = yield call(apis.getCurrentBookInfo, id);
  if (temp.code === '200') {
    return temp.data;
  }
  return ({});
}

我们试着来把 formOnMount 方法改成函数式的


/**
 * @function
 * - 弹窗首次加载
 */
function formOnMount() {
  composeAsync<void, void>(getCurrentBookInfo, form.setValues)();
}

  • 打开页面试一下效果发现不可以

换个取巧的方法实现await关键字

/**
 * @function
 * - 弹窗首次加载
 */
function formOnMount() {
  const action = composeAsync<void, void>(
      async () => await getCurrentBookInfo(), 
      form.setValues
  );
  action()
}
  • 发现仍然不可以

我们找找无法实现await关键字的原因


export const compose = <T, K>(...funcList: Function[]) => {
  if (funcList.length === 0) {
    return (arg: T) => arg as unknown as K
  }
  if (funcList.length === 1) {
    return funcList[0] as (arg: T) => K
  }
  const func = funcList.reduce((a, b) => (...args: Function[]) => b(a(...args)))
  return func as unknown as (arg: T) => K;
}
export const pipe = compose;

  • 问题就出在func上,func是通过funcListreduce方法生成的,可以看到生成的是一个普通的函数,并不支持await关键字

我们来改造一下compose方法,新写一个composeAsync

export const composeAsync = <T, K>(...funcList: Function[]) => {
  if (funcList.length === 0) {
    return async (arg: T) => await arg as unknown as K
  }
  if (funcList.length === 1) {
    return async (arg: T) => await funcList[0](arg) as (arg: T) => K
  }
  const func = funcList.reduce((a, b) => async (...args: Function[]) => await b(await a(...args)));
  return func as unknown as (arg: T) => K;
}
export const pipeAsync = composeAsync;

使用composeAsync来实现需求

/**
 * @function
 * - 弹窗首次加载
 */
function formOnMount() {
  composeAsync<void, void>(getCurrentBookInfo, form.setValues)();
}

总结

  • 你可能没意识到你实现了了一个怎样强大的基础功能性方法
  • compose把一个个函数像管子一样拼接起来,数据在管子中流动的时候可以很自然的从开头流到结尾
  • 但是composeAsync在把函数像管子一样拼接起来时,在连接处加了一个开关,如果数据在上个管道中没处理完,是不会流入到下一段管道中的
  • 这个开关就是await关键字

  • async await语法在错误捕获的时候是个短板,同样的,错误捕获在函数式编程里也不好搞
  • 写的时候注意一下,记得在数据处理失败时return一个默认值