iT邦幫忙

2025 iThome 鐵人賽

DAY 18
0
Rust

把前端加速到天花板:Rust+WASM 即插即用外掛系列 第 18

Day 18|API 契約 v2:同步入口 vs. Worker 包裝,為什麼要「腳踏兩條船」

  • 分享至 

  • xImage
  •  

到目前為止,我們的 API 大致長這樣:

  • Rust 端暴露一堆 #[wasm_bindgen] 函式,直接跑影像管線;
  • 前端可以同步呼叫,拿到 Uint8Array 回來,直接丟回 canvas;
  • 如果需要不卡 UI,就得自己寫一層 Web Worker,接收任務、呼叫 WASM、再把結果 postMessage 回主執行緒。

這個模式能用,但 API 層級有個痛點:同一套演算法核心,卻要在兩種語境裡寫兩次呼叫邏輯。同步呼叫 vs. Worker 呼叫,看起來像是兩種 API,但實際上只是封裝形式不同。

「兩條船」的設計

所謂「兩條船」:核心(Core)都用同一個,對外包裝兩層皮膚

  1. 正宮(直接呼叫)
    • apply_pipeline() / apply_pipeline_fast() 就是正宮。
    • 適合小圖、demo、測試,或不在乎 UI 阻塞的情境。
    • 呼叫即算即回傳。
  2. 小三(Worker 包裝)
    • worker.ts 裡,收到 { type:'run' } → 轉身呼叫正宮 → 再把結果傳回主執行緒。
    • 適合真實 UI:大圖、滑桿拖動、批次處理。
    • 呼叫即派工,結果回傳靠事件。

無論正宮還是小三,共用的都是同一套核心演算法。

WASM 算東西,Worker 只是換一張皮,演戲給不同場合看。

為什麼要「劈腿」?

  1. 開發情境不同

    • 想偷快、寫測試:用正宮,最直接。
    • 想經營 UI、不卡畫面:找小三出場。
    • 在不同場景扮演不同角色,卻不用改變核心。
  2. 契約保持一致

    • 不管是哪一位,給的都是同一套 API:ops 陣列進去,Uint8Array 出來。
    • 這樣不會出現「正宮有的功能小三沒有」的窘境。
  3. 錯誤訊息也一致

    • 錯誤在核心就統一成 { code, message }
    • Worker 只是把它原封不動帶回。
    • 不管你從哪邊抓包,都看得出背後都是同一個人。

API v2 的範例

Rust 端不變,仍然:

#[wasm_bindgen]
pub fn apply_pipeline(input: &[u8], w: u32, h: u32, ops: &JsValue) -> Result<Vec<u8>, JsValue> {
    // ... 核心邏輯 ...
}

前端可以:

// 正宮
const out = apply_pipeline(bytes, w, h, ops)
ctx.putImageData(new ImageData(new Uint8ClampedArray(out), w, h))

// 小三
worker.postMessage({ type:'run', w, h, ops, bytes })
worker.onmessage = ev => {
  const { ok, bytes, error } = ev.data
  if (ok) { /* set to canvas */ }
  else { /* showError(error) */ }
}

兩者呼叫的人都是一樣的:ops 陣列進去,Uint8Array 出來。

小結

「腳踏兩條船」不是亂搞,而是一種策略:

  • 一份核心,兩張臉;
  • 一邊直接陪你寫測試,一邊默默幫你撐 UI;
  • 最後兩者都遵守同一份契約,不會吵架。

這樣我們就能享受「劈腿」的好處(沒大家現實生活都要做好男人好女人喔 (つд⊂)


上一篇
Day 17|把「住在你家」的位元組管理好
系列文
把前端加速到天花板:Rust+WASM 即插即用外掛18
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言