在前一篇講了那麼多怎麼建立 Effect 跟如何執行,總覺得好像少了什麼東西,條件判斷的 if
還有像迴圈的 for
這些東西跑到哪邊去了,其實很簡單的,這邊想要介紹兩種不同風格的寫法
在這種風格中,就像之前所介紹的,會將程式拆成更多的小部份,並加以組合,之前也提到過 Effect 本身就是一個 functional 風格的套件,本身其實就已經提供了非常多 functional 風格的寫法
當你需要將資料做轉換時,你就可以用到 map
或是 flatMap
,例如我們有個包裝了 promise 的 Effect ,而你需要將結果轉換成你需要的格式時,你可以這樣做
const transformedDataEffect = pipe(
Effect.promise(() => fetchData()),
Effect.map((data) => transform(data)),
)
如果你的轉換需要使用到另一個 Effect ,例如需要用到回傳 promise 的轉換,或是需要抓另一個資料
const dataEffect = pipe(
Effect.promise(() => fetchData()),
Effect.flatMap((data) => fetchAnotherData(data.id)),
)
if
/ loop
Effect 也有提供 if
跟 loop
,可以像這樣用
const conditionalEffect = pipe(
Effect.promise(() => fetchData()),
Effect.flatMap((data) => Effect.if(data < 10, {
onTrue: () => Effect.succeed(data),
onFalse: () => Effect.fail(new Error('data is >= 10')),
}))
)
這就像是
if (data < 10) {
return Effect.succeed(data)
} else {
return Effect.fail(new Error('data is >= 10'))
}
loop 則是像這樣,例如計算從 1 到 10 ,每個數字都做平方
const effect = Effect.loop(1, {
// 判斷條件是否要繼續執行
while: (state) => state <= 10,
step: (state) => state + 1,
// 實際計算的部份
body: (state) => Effect.succeed(state * state),
})
console.log(Effect.runSync(effect)) // [1, 4, 9, ..., 100]
不過其實像這樣刻意用 functional 的方法做判斷其實有點多此一舉的感覺,因此通常我不會用到這樣的寫法,而是接下來要介紹的我比較常用的方法,若對這種寫法有興趣的可以閱讀相關的文件,文件中還有更多不同的 function
gen
使用 generator 寫 Effect這邊要介紹的是一個 js 中比較不常用的語法, generator
// 請注意這邊的 `*`
function * generator(n: number) {
for (let i = 0; i < n; i++) {
yield i
}
}
console.log(Array.from(generator(10))) // [0, 1, 2, ..., 9]
這個語法有個特色是,當執行到 yield
時,會暫停 function 的執行,並回傳 yield 指定的值,讓上層呼叫這個 generator 的決定要不要續繼執行
透過這個特性 Effect.gen
使用 generator ,讓你可以用比較接近一般的程序式的寫法也可以寫 Effect 的程式
const effect = Effect.gen(function * () {
// 注意這邊用的是 `yield*` 而不是 `yield`
const data = yield* Effect.promise(() => fetchData())
if (data < 10) {
return data
}
yield* Effect.fail(new Error('data >= 10'))
// 這邊因為上面的 `Effect.fail` 其實就不會續續執行下去了
})
你可以透過 yield*
將任何的 Effect 傳回去給 Effect 的 runtime 執行,並獲得執行的結果
Effect.gen
該如何選擇我自己平常的習慣是,若有以下情況就選擇用 Effect.gen
,若沒有則用 FP 的寫法
if
, while
(通常而言你不該用到 for
這種固定執行次數的,這在後面的 concurrency 時會講到)當然,其實兩種寫法混用也完全沒問題,例如以下範例
const effect = pipe(
Effect.gen(function * () {
const data = yield* pipe(
fetchData(),
Effect.map((data) => transform(data))
)
return data
}),
Effect.map((data) => anotherTranform(data))
)
這篇介紹了基本的邏輯判斷,與 Effect.gen
這個工具可以讓你用類似一般的程序式的寫法撰寫 Effect 的程式,在下一篇我們終於要稍微看到 Effect 可以怎麼幫助我們更好的控制流程了