解析函数中的并行承诺执行
Parallel promise execution in resolve functions
我对处理 GraphQL 客户端解析函数中的承诺有疑问。传统上,解析器将在服务器上实现,但我在客户端包装了一个 REST API。
背景和动机
给定的解析器如:
const resolvers = {
Query: {
posts: (obj, args, context) => {
return fetch('/posts').then(res => res.json());
}
},
Post: {
author: (obj, args, _, context) => {
return fetch(`/users/${obj.userId}`)
.then(res => res.json());
.then(data => cache.users[data.id] = data)
}
}
};
如果我运行查询:
posts {
author {
firstName
}
}
和Query.posts()
/posts
APIreturns四个post对象:
[
{
"id": 1,
"body": "It's a nice prototyping tool",
"user_id": 1
},
{
"id": 2,
"body": "I wonder if he used logo?",
"user_id": 2
},
{
"id": 3,
"body": "Is it even worth arguing?",
"user_id": 1
},
{
"id": 4,
"body": "Is there a form above all forms? I think so.",
"user_id": 1
}
]
Post.author()
解析器将被调用四次以解析 author
字段。
grapqhl-js
有一个非常好的特性,从 Post.author()
解析器返回的每个承诺都将并行执行。
I've further been able to eliminate re-fetching author's with the same userId
using facebook's dataloader library. BUT, I'd like to use a custom cache instead of dataloader
.
问题
有没有办法阻止 Post.author()
解析器并行执行?在 Post.author()
解析器中,我想一次获取 author
个,检查我的缓存以防止重复的 http 请求。
但是,现在从 Post.author()
返回的 promise 被排队并立即执行,所以我无法在每个请求之前检查缓存。
感谢您的任何提示!
我绝对建议您查看 DataLoader,因为它旨在解决这个问题。如果你不直接使用它,至少你可以阅读它的实现(不是那么多行)并在你的自定义缓存上借用这些技术。
GraphQL 和 graphql.js 库本身不关心加载数据 - 它们通过解析器函数将其留给您。 Graphql.js 只是尽可能急切地调用这些解析器函数,以提供最快的整体查询执行速度。您完全可以决定 return 按顺序解析的 Promises(我不推荐),或者——正如 DataLoader 实现的那样——使用 memoization 进行重复数据删除(这是你想要解决这个问题的方法)。
例如:
const resolvers = {
Post: {
author: (obj, args, _, context) => {
return fetchAuthor(obj.userId)
}
}
};
// Very simple memoization
var authorPromises = {};
function fetchAuthor(id) {
var author = authorPromises[id];
if (!author) {
author = fetch(`/users/${id}`)
.then(res => res.json());
.then(data => cache.users[data.id] = data);
authorPromises[id] = author;
}
return author;
}
仅适用于一些将 dataSource
用于 REST api 以及 dataLoader
的人(在这种情况下,它并没有真正帮助,因为它是一个单一的请求)。这是一个简单的缓存 solution/example.
export class RetrievePostAPI extends RESTDataSource {
constructor() {
super()
this.baseURL = 'http://localhost:3000/'
}
postLoader = new DataLoader(async ids => {
return await Promise.all(
ids.map(async id => {
if (cache.keys().includes(id)) {
return cache.get(id)
} else {
const postPromise = new Promise((resolve, reject) => {
resolve(this.get(`posts/${id}`))
reject('Post Promise Error!')
})
cache.put(id, postPromise, 1000 * 60)
return postPromise
}
})
)
})
async getPost(id) {
return this.postLoader.load(id)
}
}
注意:这里我使用memory-cache
作为缓存机制。
希望这有帮助。
我对处理 GraphQL 客户端解析函数中的承诺有疑问。传统上,解析器将在服务器上实现,但我在客户端包装了一个 REST API。
背景和动机
给定的解析器如:
const resolvers = {
Query: {
posts: (obj, args, context) => {
return fetch('/posts').then(res => res.json());
}
},
Post: {
author: (obj, args, _, context) => {
return fetch(`/users/${obj.userId}`)
.then(res => res.json());
.then(data => cache.users[data.id] = data)
}
}
};
如果我运行查询:
posts {
author {
firstName
}
}
和Query.posts()
/posts
APIreturns四个post对象:
[
{
"id": 1,
"body": "It's a nice prototyping tool",
"user_id": 1
},
{
"id": 2,
"body": "I wonder if he used logo?",
"user_id": 2
},
{
"id": 3,
"body": "Is it even worth arguing?",
"user_id": 1
},
{
"id": 4,
"body": "Is there a form above all forms? I think so.",
"user_id": 1
}
]
Post.author()
解析器将被调用四次以解析 author
字段。
grapqhl-js
有一个非常好的特性,从 Post.author()
解析器返回的每个承诺都将并行执行。
I've further been able to eliminate re-fetching author's with the same
userId
using facebook's dataloader library. BUT, I'd like to use a custom cache instead ofdataloader
.
问题
有没有办法阻止 Post.author()
解析器并行执行?在 Post.author()
解析器中,我想一次获取 author
个,检查我的缓存以防止重复的 http 请求。
但是,现在从 Post.author()
返回的 promise 被排队并立即执行,所以我无法在每个请求之前检查缓存。
感谢您的任何提示!
我绝对建议您查看 DataLoader,因为它旨在解决这个问题。如果你不直接使用它,至少你可以阅读它的实现(不是那么多行)并在你的自定义缓存上借用这些技术。
GraphQL 和 graphql.js 库本身不关心加载数据 - 它们通过解析器函数将其留给您。 Graphql.js 只是尽可能急切地调用这些解析器函数,以提供最快的整体查询执行速度。您完全可以决定 return 按顺序解析的 Promises(我不推荐),或者——正如 DataLoader 实现的那样——使用 memoization 进行重复数据删除(这是你想要解决这个问题的方法)。
例如:
const resolvers = {
Post: {
author: (obj, args, _, context) => {
return fetchAuthor(obj.userId)
}
}
};
// Very simple memoization
var authorPromises = {};
function fetchAuthor(id) {
var author = authorPromises[id];
if (!author) {
author = fetch(`/users/${id}`)
.then(res => res.json());
.then(data => cache.users[data.id] = data);
authorPromises[id] = author;
}
return author;
}
仅适用于一些将 dataSource
用于 REST api 以及 dataLoader
的人(在这种情况下,它并没有真正帮助,因为它是一个单一的请求)。这是一个简单的缓存 solution/example.
export class RetrievePostAPI extends RESTDataSource {
constructor() {
super()
this.baseURL = 'http://localhost:3000/'
}
postLoader = new DataLoader(async ids => {
return await Promise.all(
ids.map(async id => {
if (cache.keys().includes(id)) {
return cache.get(id)
} else {
const postPromise = new Promise((resolve, reject) => {
resolve(this.get(`posts/${id}`))
reject('Post Promise Error!')
})
cache.put(id, postPromise, 1000 * 60)
return postPromise
}
})
)
})
async getPost(id) {
return this.postLoader.load(id)
}
}
注意:这里我使用memory-cache
作为缓存机制。
希望这有帮助。