Eric Lau

Next.js Promise Racing:如何優化伺服器端元件的載入體驗

約 5 分鐘
Next.js Promise Racing:如何優化伺服器端元件的載入體驗

在現代網頁開發中,使用者體驗和頁面載入速度已成為開發者最關注的重點之一。Next.js 作為一個強大的 React 框架,不斷推出創新功能來解決這些挑戰。今天,我們將深入探討一個非常有趣且實用的 Next.js 技術——Promise Racing,這項技術能夠智能平衡伺服器處理時間和客戶端等待體驗。

Promise Racing 的概念與應用

Promise Racing 是一種程式設計模式,允許我們同時執行多個 Promise,並在其中任何一個完成時立即得到結果。在 Next.js 中,我們可以利用這個模式來實現一個極具創意的功能:「在伺服器上完全解析 Promise,除非它花費超過 N 秒。如果超時,則在客戶端顯示載入動畫。」

這個功能特別適合那些資料獲取可能需要一些時間,但我們不希望使用者等待太久的情境。讓我們來看一個實際例子:

import { Suspense } from "react";// 模擬非同步資料獲取函數function fetchUser() {  return new Promise((resolve) => {    setTimeout(() => {      resolve({ name: "Corbin Crutchley" });    }, 2000);  });}// 將傳入的 promise 與 1 秒超時競賽function race(promise) {  return Promise.any([    promise,    new Promise((resolve) => setTimeout(() => resolve(), 1000)),  ]);}async function UserDisplay({ promise }) {  const user = await promise;  return {user.name};}export default async function Page() {  // 開始獲取使用者資料  const userPromise = fetchUser();    // 如果使用者資料獲取超過 1 秒,我們不會等待它  // 而是渲染一個備用 UI。  await race(userPromise);    return (    Loading...}>            );}

深入理解 Promise Racing 的工作原理

上面的程式碼示例看起來相當簡單,但其背後的工作原理十分精妙。讓我們逐步分析:

  1. 資料獲取初始化:我們首先創建一個 Promise(在此例中是 fetchUser()),它代表了我們想要獲取的資料。
  2. 競賽設置:使用 race() 函數,我們讓實際的資料獲取 Promise 與一個 1 秒的計時器 Promise 競賽。
  3. 伺服器等待決策:伺服器只會等待這個競賽的結果。如果資料在 1 秒內返回,伺服器會等待完成;如果超過 1 秒,伺服器會「放棄等待」並繼續處理請求。
  4. Suspense 處理:無論伺服器是否等待完成,我們都設置了一個 Suspense 邊界,它能在客戶端優雅地處理尚未完成的 Promise。

最令人驚嘆的是,這一切都是使用React 伺服器元件 (RSCs)實現的,完全不需要 "use client" 指令!我們在伺服器端使用了傳統上僅限客戶端的 <Suspense> 元件來處理資料載入,展示了 React 在客戶端和伺服器端 API 的完美融合。

為普通使用者解釋 Promise Racing

如果你對程式設計不太熟悉,可以將 Promise Racing 想像成一場特殊的比賽:

想像你去一家餐廳點了一道需要時間準備的主菜。餐廳告訴你:「我們會盡快準備您的主菜,但如果超過 5 分鐘還沒準備好,我們會先給您上一些開胃菜,讓您不必空著肚子等待。」

在這個比喻中:

  • 主菜 = 您真正想要的資料
  • 5 分鐘 = 伺服器願意等待的時間
  • 開胃菜 = 載入動畫或臨時 UI

這種方法確保了:

  • 如果資料很快就準備好了,您會直接得到完整的頁面
  • 如果資料準備時間較長,您會先看到一個過渡界面,然後在資料準備好時自動更新

這正是現代網頁設計的精髓:儘可能快速地為使用者提供有用的內容,並在背景繼續改善體驗。

透過網路傳輸 Promise 的技術奇蹟

這個技術最令人著迷的一點是,我們實際上在做一件看似不可能的事情:將一個 Promise(一個運行中的非同步操作)從伺服器「序列化」並傳送到客戶端。這突顯了 JSX-over-the-wire 的概念,我們能夠序列化 Promise 並透過網路傳送,而且是有條件地傳送!

這意味著:

  • 如果 Promise 在設定的時間內(例如 1 秒)完成,客戶端將永遠不會收到載入指示器的 JSX。
  • 如果沒有及時完成,Promise 將通過 Next.js 的 RPC 機制「繼續」在客戶端執行。

在我們的示例中,fetchUser 需要 2 秒才能完成,但等待時間僅為 1 秒。這意味著使用者只需額外等待 1 秒(而不是完整的 2 秒),就能看到完整的資料。這不僅是一個有趣的技術展示,更帶來了實際的效能優勢。

Promise Racing 的實際應用場景

這種技術在哪些情況下特別有用呢?以下是一些實際應用場景:

  1. 資料儀表板:顯示來自多個 API 的資料,但在超過設定時間後先顯示部分已加載的資料。
  2. 電子商務產品頁面:在產品基本資訊載入後立即顯示,而產品評論、相關產品等次要資訊可以稍後載入。
  3. 社群媒體摘要:顯示最新貼文,同時在背景載入更多內容和互動數據。
  4. 搜尋結果頁面:快速顯示最相關的結果,同時繼續處理更多精細的搜尋結果。

這種方法極大地改善了感知性能,讓使用者覺得您的應用程式響應更快,即使實際資料載入時間相同。

效能優勢與注意事項

實施 Promise Racing 帶來的主要效能優勢包括:

  • 減少感知延遲:使用者不必等待所有資料都準備好才能與頁面互動。
  • 提高伺服器吞吐量:伺服器可以更快地完成請求處理,為更多使用者提供服務。
  • 漸進式載入:使用者體驗一個漸進增強的界面,而不是長時間的空白畫面。

然而,實施時也有一些注意事項:

  • 資源使用:Promise 在客戶端繼續執行可能會增加客戶端的資源消耗。
  • 重複請求:需要小心避免在伺服器和客戶端重複發起相同的請求。
  • 狀態同步:當 Promise 在客戶端解析時,需要確保狀態正確同步。

結語與未來展望

Next.js 的 Promise Racing 技術展示了現代網頁框架如何巧妙地平衡伺服器處理和客戶端體驗。這不僅是一個酷炫的技術展示,更是一種實用的方法來提升應用程式的感知性能和使用者滿意度。

隨著 React 伺服器元件生態系統的不斷發展,我們可以期待更多類似的創新模式出現,進一步模糊伺服器和客戶端之間的界限,為開發者提供更多構建高性能、用戶友好應用程式的工具。

Next.js 的這種優雅解決方案展示了現代前端框架不僅關注於程式碼的優雅性,更注重實際使用者體驗的優化。透過像 Promise Racing 這樣的技術,開發者可以在保持程式碼簡潔的同時,為使用者提供更快、更流暢的網頁體驗。

您是否有興趣嘗試這項技術?或者您已經在項目中實施了類似的模式?無論您是資深開發者還是剛剛接觸 Next.js,這種技術都值得加入您的開發工具箱中。