透過串流方式立即回應

任何使用過服務工作者的人都會告訴您,服務工作者是完全非同步的。這些類別只會依賴 FetchEvent 等事件式介面,並使用承諾來指出非同步作業何時完成。

在服務工作的擷取事件處理常式提供的回應方面,非同步性同樣重要,但開發人員較少會注意到這點。這裡的金標準是串流回應:只要有第一個資料區塊可用,就會允許發出原始要求的網頁開始處理回應,並可能使用針對串流最佳化的剖析器,逐步顯示內容。

編寫自己的 fetch 事件處理常式時,通常只需透過 fetch()caches.match() 傳遞 respondWith() 方法的 Response (或 Response 的承諾),並每天呼叫該方法。好消息是,這兩種方法建立的 Response 已經可供串流播放!壞消息是,至少到目前為止,「手動」建構Response 無法串流。這時就需要Streams API 出馬了。

串流?

串流是可逐步建立及操作的資料來源,並提供介面,可讀取或寫入非同步資料區塊,但在任何特定時間,只有其中一部分可能會在記憶體中提供。目前我們只會使用 ReadableStream,用於建構傳遞至 fetchEvent.respondWith()Response 物件:

self.addEventListener('fetch', event => {
    var stream = new ReadableStream({
    start(controller) {
        if (/* there's more data */) {
        controller.enqueue(/* your data here */);
        } else {
        controller.close();
        }
    });
    });

    var response = new Response(stream, {
    headers: {'content-type': /* your content-type here */}
    });

    event.respondWith(response);
});

只要 event.respondWith() 一呼叫,要求觸發 fetch 事件的網頁就會立即收到串流回應,只要服務工作者持續 enqueue()其他資料,就會持續從該串流讀取資料。從服務工作者傳送至網頁的回應確實為非同步,我們可以完全控管填入串流!

實際應用

您可能注意到,先前的範例含有 /* your data here */ 預留位置註解,且沒有提供實際實作詳細資訊。那麼實際範例會是什麼樣子呢?

Jake Archibald (毫不意外) 提供了絕佳範例,說明如何使用串流將多個快取 HTML 片段的 HTML 回應,以及透過 fetch() 串流傳送的「即時」資料拼接在一起,在本例中,就是他的網誌內容

Jake 解釋,使用串流回應的好處在於瀏覽器可以剖析及算繪串流中的 HTML,包括從快取中快速載入的初始位元,而無須等待整個部落格內容擷取作業完成。這可充分利用瀏覽器的漸進式 HTML 算繪功能。其他可漸進轉譯的資源 (例如某些圖片和影片格式) 也能從這項做法中受益。

串流?或應用程式殼?

目前有關使用服務工作者為網路應用程式提供動力的最佳做法,主要著重於應用程式命令介面 + 動態內容模型。這種做法會積極快取網頁應用程式的「殼層」,也就是顯示結構和版面配置所需的 HTML、JavaScript 和 CSS 最小值,然後透過用戶端要求載入每個特定網頁所需的動態內容。

串流可提供應用程式殼層模型的替代方案,在這種情況下,當使用者前往新頁面時,瀏覽器會收到完整的 HTML 回應串流。串流回應可使用快取資源,因此即使在離線時,仍可快速提供 HTML 的初始區塊!但最終還是會顯示類似傳統伺服器轉譯回應內容的樣式。舉例來說,如果您的網頁應用程式由內容管理系統提供動力,而該系統會透過拼接部分範本來進行伺服器算繪 HTML,則該模型會直接轉譯為使用串流回應,並在服務工作者 (而非伺服器) 中複製範本邏輯。如以下影片所示,在該用途中,串流回應提供的速度優勢相當顯著:

串流傳輸整個 HTML 回應的一大優點 (也是影片中提到的速度最快的替代方案) 是,在初始導覽要求期間算繪的 HTML 可充分利用瀏覽器的串流 HTML 剖析器。在網頁載入後插入文件的 HTML 片段 (在應用程式殼層模型中很常見) 無法利用這項最佳化功能。

因此,如果您正處於服務工作者導入作業的規劃階段,應採用哪種模型:逐步算繪的串流回應,還是與動態內容的用戶端要求耦合的輕量化殼層?答案不出所料,取決於您是否有現有的實作項目,該項目依賴 CMS 和部分範本 (優點:串流);取決於您是否預期單一大型 HTML 酬載可從漸進式算繪中受益 (優點:串流);取決於您的網路應用程式是否最適合以單頁應用程式建模 (優點:應用程式殼層);以及取決於您是否需要目前在多個瀏覽器的穩定版本中皆支援的模型 (優點:應用程式殼層)。

服務工作者輔助串流回應功能仍處於初期階段,我們期待各種模型日趨成熟,尤其是更多工具開發完成,以便自動化常見的用途。

深入瞭解串流

如果您要自行建構可讀取的串流,只是隨意呼叫 controller.enqueue() 可能不夠充分或有效率。Jake 將詳細說明如何搭配使用 start()pull()cancel() 方法,建立符合您用途的資料串流。

如需進一步瞭解,請參閱 Streams 規範

相容性

Chrome 52 新增了支援功能,可在服務工作者中使用 ReadableStream 做為來源,建構 Response 物件。

Firefox 的服務工作者實作方式尚不支援由 ReadableStream 支援的回應,但有相關的 追蹤錯誤,可讓您追蹤 Streams API 支援。

如要追蹤 Edge 中未加上前置字元的 Streams API 支援進度,以及整體Service Worker 支援,請前往 Microsoft 的平台狀態頁面