問題
TypeScript 生態系中的大多數韌性函式庫都綁定在 HTTP 用戶端(axios-retry、p-retry、Polly.js),或是要求你把函式包裝在類別中並使用 .execute() 的繁瑣方式。如果你只是想把重試邏輯、逾時與斷路器套用到 任何 async 函式——資料庫查詢、內部服務呼叫、檔案操作——而不引入沉重的依賴,該怎麼辦?
在此隆重介紹 houhou。
什麼是 houhou?
Houhou 是一個零依賴的 TypeScript 函式庫(約 500 行程式碼),可透過可組合的韌性策略包裝任何 async 函式。包裝後的函式保留 完全相同的簽章——你可以像呼叫原始函式一樣呼叫它。
import { task } from 'houhou'
const charge = task(chargeCard)
.retry(3)
.timeout(10_000)
.fallback(() => ({ status: 'pending' }))
await charge(account, amount)
Enter fullscreen mode Exit fullscreen mode
策略一覽
重試
使用固定或指數退避重新執行失敗的動作:
task(fetchUser).retry(3) // 簡寫
task(fetchUser).retry({
attempts: 5,
backoff: 'exponential',
jitter: true,
delay: 500
})
Enter fullscreen mode Exit fullscreen mode
逾時
若函式未在指定時間內完成則拒絕:
task(fetchUser).timeout(5000)
Enter fullscreen mode Exit fullscreen mode
備援
在失敗時執行替代函式:
task(fetchUser).fallback(() => loadFromCache(id))
Enter fullscreen mode Exit fullscreen mode
斷路器
避免重複呼叫不健康的服務:
task(queryDb).circuitBreaker({
failureThreshold: 5,
successThreshold: 2,
resetTimeout: 30_000
})
Enter fullscreen mode Exit fullscreen mode
延遲
在執行前等待:
task(syncData).delay(1000)
Enter fullscreen mode Exit fullscreen mode
策略順序很重要
策略是 巢狀包裝 的:最後呼叫的方法會包裝前面的方法。執行順序與宣告順序相反。
task(fn).retry(3).timeout(1000)
// → timeout 包裝 retry
// → 函式執行 → 失敗時重試(最多 3 次)→ 總逾時 1 秒
// → 若逾時觸發,就不會再有重試
Enter fullscreen mode Exit fullscreen mode
task(fn).timeout(1000).retry(3)
// → retry 包裝 timeout
// → 函式執行 → 1 秒逾時 → 若逾時發生,retry 會捕獲它
// → 整個循環最多重複 3 次
Enter fullscreen mode Exit fullscreen mode
型別安全 — 策略鎖定
每個方法都是 可鎖定 的。TypeScript 會在編譯時期防止重複設定同一策略,執行時則由 Set 防護機制強制執行:
const t = task(fn).retry(3)
// @ts-expect-error — 'retry' 已被鎖定
t.retry(2) // 執行時也會拋出錯誤
Enter fullscreen mode Exit fullscreen mode
使用 AbortSignal 取消
Houhou 使用 AbortController 在逾時觸發或提供外部訊號時取消操作。AbortSignal 會作為函式的最後一個參數傳入:
const fn = (url: string, signal?: AbortSignal) => fetch(url, { signal })
task(fn).timeout(5000)('https://api.example.com')
// → 逾時觸發 → controller.abort() → fetch 被取消
Enter fullscreen mode Exit fullscreen mode
你也可以傳入自己的外部訊號:
const controller = new AbortController()
const promise = task(fn).timeout(5000).retry(3)('url', controller.signal)
controller.abort() // 取消所有操作
Enter fullscreen mode Exit fullscreen mode
組合範例
策略可以以任意順序流暢地串接:
const resilient = task(callApi)
.retry({ attempts: 3, backoff: 'exponential' })
.timeout(5000)
.fallback(loadFromCache)
.circuitBreaker({ failureThreshold: 5, successThreshold: 2, resetTimeout: 30_000 })
.delay(100)
Enter fullscreen mode Exit fullscreen mode
名稱由來
「houhou」在日文中意指「方法」或「做法」——非常適合用來描述如何執行你的函式。
立即試用
npm install houhou
# 或
pnpm add houhou
yarn add houhou
Enter fullscreen mode Exit fullscreen mode
GitHub: https://github.com/smokeeaasd/houhou
npm: https://npmjs.com/package/houhou
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.