Effect.disconnect
:讓 uninterruptible Effect 逾時時先回應、後收尾在 Effect 中,Effect.disconnect
讓不可被中斷的工作在背景繼續執行。且不阻擋外部逾時的發生。使外部流程會在逾時當下先拿到逾時的錯誤結果,讓流程可以繼續走下去。
我們有一個需要 2 秒才能完成的工作,但我們希望它能在 1 秒內超時。這樣我們才能測試Effect.disconnect
的效果。
import { Effect } from "effect";
const formatTime = (date: Date) => {
return date.toLocaleString("zh-TW", {
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: false
})
}
const longRunningTask = () =>
Effect.gen(function*() {
console.log(`[斷線示範] 開始工作 (${formatTime(new Date())})`)
yield* Effect.sleep("2 seconds")
console.log(`[斷線示範] 工作完成(在背景收尾) (${formatTime(new Date())})`)
}).pipe(Effect.uninterruptible) // 關鍵:讓任務不可中斷
這裡要注意的點是,我們有使用 Effect.uninterruptible
來讓任務不可中斷。因為Effect.disconnect
是需要與 Effect.uninterruptible
一起使用才能發揮作用的。
Effect.disconnect
。觀察結果是否會被阻擋住,直到工作完成,才拿到逾時的錯誤結果。Effect.disconnect
。觀察結果是否會在逾時當下先拿到逾時的錯誤結果,讓流程可以繼續走下去。我們直接來看看程式碼的測試結果吧!
Effect.disconnect
的測試結果// 測試 1: 沒有 Effect.disconnect
console.log("=== 測試 1: 沒有 Effect.disconnect ===")
const exit1 = await Effect.runPromiseExit(
longRunningTask().pipe(Effect.timeout("1 second"))
)
console.log("結果:", exit1._tag)
// 等待一下看看是否有背景任務
console.log("等待 3 秒看是否有背景任務...")
await new Promise((resolve) => setTimeout(resolve, 3000))
/*
輸出:
[斷線示範] 開始工作 (2025/09/29 02:58:46)
[斷線示範] 工作完成(在背景收尾) (2025/09/29 02:58:48)
結果: Failure
等待 3 秒看是否有背景任務..
*/
Effect.disconnect
的測試結果// 測試 2: 有 Effect.disconnect
console.log("\n=== 測試 2: 有 Effect.disconnect ===")
const timedEffect = longRunningTask().pipe(
Effect.disconnect, // 關鍵:允許工作在背景完成
Effect.timeout("1 second")
)
const exit2 = await Effect.runPromiseExit(timedEffect)
console.log("結果:", exit2._tag)
// 等待一下讓背景任務完成
console.log("等待 3 秒讓背景任務完成...")
await new Promise((resolve) => setTimeout(resolve, 3000))
/*
輸出:
[斷線示範] 開始工作 (2025/09/29 02:59:25)
結果: Failure
等待 3 秒讓背景任務完成...
[斷線示範] 工作完成(在背景收尾) (2025/09/29 02:59:27)
*/
測試情境 | 使用 Effect.disconnect |
執行時間 | 結果取得時機 | 背景任務完成 | 觀察重點 |
---|---|---|---|---|---|
測試 1 | ❌ 沒有使用 | 2 秒後 | 工作完成後才取得逾時錯誤 | 無背景任務 | 被不可中斷任務阻擋,直到完成才回傳逾時錯誤 |
測試 2 | ✅ 有使用 | 1 秒後 | 逾時當下立即取得錯誤 | 有背景任務繼續執行 | 外部流程不被阻擋,背景任務繼續收尾 |
Effect.timeoutTo 比 Effect.timeout 更靈活,它允許你為成功和超時兩種情況定義不同的結果。這在你想根據操作是否在時間內完成來客製化結果時非常有用。
Effect.timeoutTo({
duration: Duration, // 超時時間
onSuccess: (result) => T, // 成功時的回調函數
onTimeout: () => T // 超時時的回調函數
})
import { Effect, Either } from "effect"
const task = Effect.gen(function*() {
console.log("Start processing...")
yield* Effect.sleep("2 seconds") // Simulates a delay in processing
console.log("Processing complete.")
return "Result"
})
const program = task.pipe(
Effect.timeoutTo({
duration: "1 second",
onSuccess: (result) => Either.right(result),
onTimeout: () => Either.left("Timed out!")
})
)
Effect.runPromise(program).then(console.log)
/**
* 輸出:
* Start processing...
* { _id: 'Either', _tag: 'Left', left: 'Timed out!'}
*
*/
特性 | Effect.timeout | Effect.timeoutTo |
---|---|---|
超時處理 | 拋出 TimeoutException | 自定義超時結果 |
成功處理 | 返回原始結果 | 自定義成功結果 |
靈活性 | 較低 | 較高 |
使用場景 | 簡單超時控制 | 複雜業務邏輯 |
結論: Effect.timeoutTo
是處理需要根據超時狀態提供不同結果的場景的最佳選擇!
API | 逾時行為 | 適用場景 |
---|---|---|
timeout |
拋出 TimeoutException |
簡單逾時控制 |
timeoutOption |
返回 None |
可選性操作 |
timeoutTo |
自定義結果 | 複雜業務邏輯 |
Effect.disconnect
:外部流程可以在逾時當下先拿到逾時的錯誤結果,讓流程可以繼續走下去。