GraphQL LogoGraphQL

使用 @defer 和 @stream 指令改善延遲

2020 年 12 月 8 日作者Rob Richard、Liliana Matos

Rob Richard 和 Liliana Matos 是 1stDibs.com 的前端工程師。他們一直與 GraphQL 工作小組合作,擔任 @defer@stream 指令的倡導者。

自從 Lee Byron 在 2016 年 GraphQL 歐洲大會 上首次談論 @defer@stream 指令以來,它們就一直備受期待。在 2020 年的大部分時間裡,我們一直與 GraphQL 工作小組合作,以標準化此功能。它現在是第 2 階段提案,但為了進一步推進,我們希望 GraphQL 社群嘗試使用這些指令並提供回饋。我們已發布 GraphQL.jsexpress-graphql 的實驗版本。它們在 npm 上以 graphql@experimental-stream-deferexpress-graphql@experimental-stream-defer 發布。我們鼓勵所有對此功能感興趣的人試用這些版本,並在 為回饋建立的問題 中讓我們知道進度。繼續閱讀以進一步了解此提案提供的內容。

GraphQL 請求/回應模型的缺點之一是,在整個請求處理完成之前,不會將 GraphQL 回應傳回給客戶端。但是,並非所有請求的資料都同等重要,在某些使用案例中,應用程式有可能針對請求資料的子集採取行動。如果 GraphQL 伺服器可以在準備好時立即傳送最重要的資料,應用程式可以縮短其互動時間。新的 @defer@stream 指令允許 GraphQL 伺服器執行此操作,方法是從單一 GraphQL 回應傳回多個酬載。

@defer 指令可以套用至片段散佈和內嵌片段。這是開發人員宣告查詢部分為非立即回傳必要的宣告方式。

以下是 @defer 指令的範例

請求#

query {
person(id: "cGVvcGxlOjE=") {
name
...HomeworldFragment @defer(label: "homeworldDefer")
}
}
fragment HomeworldFragment on Person {
homeworld {
name
}
}

回應#

酬載 1

{
"data": {
"person": {
"name": "Luke Skywalker"
}
},
"hasNext": true
}

酬載 2

{
"label": "homeworldDefer",
"path": ["person"],
"data": {
"homeworld": {
"name": "Tatooine"
}
},
"hasNext": false
}

當 GraphQL 執行引擎遇到 @defer 指令時,它會分岔執行並開始非同步解析那些欄位。在遞延酬載仍準備中的同時,客戶端可以接收並針對初始酬載採取行動。當遞延資料龐大、載入成本高昂或不在互動性的關鍵路徑上時,這項功能最為有用。

@defer 類似,@stream 指令也允許客戶端在整個結果準備好之前接收資料。@stream 可以用於清單欄位。以下是 @stream 指令的範例

請求#

query {
person(id: "cGVvcGxlOjE=") {
name
films @stream(initialCount: 2, label: "filmsStream") {
title
}
}

回應#

酬載 1

{
"data": {
"person": {
"name": "Luke Skywalker",
"films": [
{ "title": "A New Hope" },
{ "title": "The Empire Strikes Back" }
]
}
},
"hasNext": true
}

酬載 2

{
"label": "filmsStream",
"path": ["person", "films", 2],
"data": {
"title": "Return of the Jedi"
},
"hasNext": true
}

酬載 3

{
"label": "filmsStream",
"path": ["person", "films", 3],
"data": {
"title": "Revenge of the Sith"
},
"hasNext": false
}

當 GraphQL 執行引擎遇到 @stream 指令時,它將解析由 initialCount 參數指定的許多清單項目。其餘項目將會非同步解析。這對於只能在摺疊上方呈現少數元素的介面特別有用。伺服器仍在解析其餘資料時,客戶端可以盡快呈現這些元素。

儘管 GraphQL 規範未指定傳輸協定,我們預期具有 @defer/@stream 的查詢最常見的傳輸將是具有分塊傳輸編碼的 HTTP。這允許 GraphQL 伺服器保持標準 HTTP 連線開啟,同時在準備就緒時將每個酬載串流傳輸到客戶端。它具有低負載,數十年來一直受到瀏覽器的支援,並且可以輕鬆與大多數基礎架構搭配使用。

您可以在以下位置瞭解更多關於這些指令的資訊

Rob RichardLiliana Matos,前端工程,1stDibs.com