GraphQL LogoGraphQL

使用 @defer 和 @stream 指令提升延迟

2020 年 12 月 8 日 由 Rob Richard, Liliana Matos

Rob Richard 和 Liliana Matos 是 1stDibs.com 的前端工程师。他们一直与 GraphQL 工作组合作,作为 @defer@stream 指令的倡导者。

@defer@stream 指令一直是备受期待的功能,自从 Lee Byron 在 2016 年 GraphQL 欧洲大会 上首次谈论它以来。在 2020 年的大部分时间里,我们一直在与 GraphQL 工作组合作,以标准化此功能。它现在是一个第二阶段提案,但为了进一步推进,我们希望 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 Richard, Liliana Matos, 前端工程,1stDibs.com