深入淺出 Promise 與 React Suspense

一個關於「永遠等待」的 Promise 的互動故事

1

一切的起點:正常的 Promise

在深入之前,我們先複習一下 Promise 是什麼。想像它是一個「承諾」,保證在未來某個時間點會給你一個結果,可能是「成功」或「失敗」。

const myPromise = new Promise((resolve, reject) => {
  // 這裡執行一些需要時間的工作,例如跟伺服器要資料
  // ... 工作完成後 ...
  // 如果成功,就呼叫 resolve()
  // 如果失敗,就呼叫 reject()
});

下面我們來模擬一下。點擊按鈕,看看這個 Promise 的狀態如何變化:

狀態: ⏳ 等待中 (Pending)
2

一個奇特的 Promise:() => {}

現在,來看我們今天的主角。如果我們給 `new Promise` 一個空的函式會發生什麼事?

const endlessPromise = new Promise(() => {});

// 這個函式裡面...
// - 沒有呼叫 resolve()
// - 沒有呼叫 reject()
// - 什麼事都沒做!

這個 Promise 就像一個說了「我保證」卻從不給出結果的朋友。它既不說成功,也不說失敗。因此,它會永遠處於「等待中」(Pending) 的狀態。點擊下方按鈕來建立它,你會發現它的狀態永遠不會改變。

狀態: ⏳ 等待中 (Pending)
3

終極應用:React Suspense 的魔法

React Suspense 是一個聰明的機制。當一個元件在渲染時需要等待非同步資料,它可以「暫停」渲染,並顯示一個備用的載入畫面 (Fallback UI)。

它的工作原理是:如果一個元件在渲染過程中「丟出 (throw)」一個 Promise,Suspense 就會捕捉到它,然後等待這個 Promise 完成。

function MyComponent() {
  const data = useData(); // 假設這個 Hook 在資料未準備好時...
  return <div>{data}</div>;
}

function useData() {
  if (!dataIsReady) {
    // ...就丟出一個 Promise 來「暫停」渲染!
    throw new Promise(resolve => { /* ...去載入資料... */ });
  }
  return data;
}

// React 會像這樣處理
<Suspense fallback={<LoadingSpinner />}>
  <MyComponent />
</Suspense>

現在,讓我們來看看當我們丟出「永遠等待的 Promise」時會發生什麼事:

React Suspense 模擬器

點擊下方按鈕開始渲染元件

🎉

總結

`throw new Promise(() => {});` 這行程式碼的威力在於:

  • 它建立了一個永遠不會結束的 Promise
  • 當在 React 元件中丟出 (throw) 這個 Promise 時...
  • React Suspense 會捕捉到它,並忠實地永遠顯示 Fallback 載入畫面

這是一種強大的技巧,可以用於某些特殊場景,例如在 Storybook 中展示元件的載入狀態,或者在特定情況下故意讓一部分 UI 保持在載入中。現在,您已經完全掌握這個概念了!