qiuhongbingo / blog

Writing something in Issues.
https://github.com/qiuhongbingo/blog/issues
3 stars 0 forks source link

理解函数式编程中的「代数效应」 #40

Open qiuhongbingo opened 3 years ago

qiuhongbingo commented 3 years ago
/**
 * 0.
 * 假设我们有一个函数 getPicTotal
 * 传入两个用户名称后,分别查找用户的图片数量
 * 最后将图片数量相加返回
 * @param {*} name1
 * @param {*} name2
 */
function getPicTotal(name1, name2) {
  const num1 = getPicNum(name1)
  const num2 = getPicNum(name2)

  return num1 + num2
}

getPicTotal()

/**
 * 1.
 * 上面是同步的方法,如果 getPicNum 需要请求服务器来获取数据
 * 那么我们需要改成异步调用
 */
async function getPicTotal(name1, name2) {
  const [num1, num2] = await Promise.all([
    getPicNum(name1),
    getPicNum(name2)
  ])

  return num1 + num2
}

await getPicTotal()

/**
 * 2.
 * 但是,当一个函数变为 async 后,意味着调用它的函数也需要 await
 * 这破坏了 getPicTotal 的同步特性
 * 有没有什么办法能让 getPicTotal 在保持现有调用方式不变的情况下实现异步请求呢?
 * 以下为伪代码实现
 */
function getPicTotal(name1, name2) {
  const num1 = getPicNum(name1)
  const num2 = getPicNum(name2)

  return num1 + num2
}

function getPicNum(name) {
  /**
   * 执行 perform name 时
   * 函数调用栈会从 getPicNum 方法内跳出
   * 被外层的 try handle 捕获
   * 这有点类似 throw error 后被 try catch 捕获,但不同的是,这里的调用栈暂时不会被销毁
   */
  const num = perform name
  return num
}

try {
  getPicTotal('小明', '小华')
} handle (who) { // try handle 是虚构的语法,但足以演示代数效应的思想
  switch (who) {
    case '小明':
      // who === getPicNum perform 的 name 值
      // 执行完 resume with 后,调用栈会回到 getPicNum,此时 num === 1
      resume with 1
    case '小华':
      resume with 2
    default:
      resume with 0
  }
}

/**
 * 总结:代数效应能够将副作用(例子中为请求图片数量)从函数逻辑中分离,使函数关注点保持纯粹
 * 而代数效应在 react 中的应用便是 hooks,不需要区分 num 是异步还是同步
 */
function App() {
  const [num, updateNum] = useState(0)
  return <button onClick={() => updateNum((num) => num + 1)}>{num}</button>
}