點燈坊

失くすものさえない今が強くなるチャンスよ

使用 then() 讓 Promise 也能 Pipeline

Sam Xiao's Avatar 2020-11-19

ECMAScript 2015 的 Promise 自帶 then(),讓我們能類似 Functor 的 map() 或 Monad 的 chain() 直接修改 Promise 內部資料,唯 then() 是 Method Chaining 而不是 Function Pipeline,因此 Ramda 提供 Function 版的 then()

Version

Ramda 0.27.1

Promise

let data = 1

let f = x => Promise.resolve(x)

f(1)
  .then(x => x * 2)
  .then(x => x + 1)
  .catch(console.error) // ?

Promise 具有 Functor 特性,因此可將 then()map() 用。

then000

then()

import { pipe, andThen as then, otherwise } from 'ramda'
import { resolve } from 'wink-fp'

let data = 1

let f = x => resolve(x)

pipe(
  f,
  then(x => x * 2),
  then(x => x + 1),
  otherwise(console.error)
)(1) // ?

但可惜 .then() 為 Method Chaining 而無法 Function Pipeline。

Ramda 另外提供了 andThen() ,此為 then() 的 function 版本。

andThen()
(a → b) → (Promise e a) → (Promise e b)

a -> b:projection function

Promise e a:data 為 Promise

Promise e b:回傳全新 Promise

Ramda 在 0.26.1 之前使用 then(),後來改成 andThen() 較冗長,個人習慣將 andThen() 改成 then() 使用

then001

Promise Monad

import { pipe, andThen as then, otherwise } from 'ramda'
import { resolve } from 'wink-fp'

let data = 1

let f = x => resolve(x)

pipe(
  f,
  then(x => x * 2),
  then(x => x + 1),
  then(x => resolve(x)),
  otherwise(console.error)
)(1) // ?

Promise 是 Moand,除了可把 then()map() 使用外,也可當成 chain() 使用,儘管回傳 Promise,最後也會被 flatten() 掉成為只有一層 Promise。

then002

Composition Law

import { pipe, andThen as then, otherwise } from 'ramda'
import { resolve } from 'wink-fp'

let data = 1

let f = x => resolve(x)

let g = pipe(
  x => x * 2,
  x => x + 1
)

pipe(
  f,
  then(g),
  otherwise(console.error)
)(1) // ?

因為 Promise 為 Moand,而 Moand 又是 Functor,因此支援 composition law,可將所有 then() 的 callback 組合成 function 後,再一次傳給 then() 即可。

then003

Conclusion

  • Ramda 0.26.1 的 then() 在 0.27.0 改成 andThen(),但所有功能都一樣,目的是讓 Promise 也能 pipe()compose(),個人習慣將 andThen() alias 成 then() 使用較精簡

Reference

Ramda, andThen()