我有一个关于在GraphQL客户端的解析函数中处理promise的问题。传统上,解析器将在服务器上实现,但我在客户端上包装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
API会返回四个帖子对象:
[
{
"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()
解析器返回的每个promise将并行执行。
我还能够使用Facebook的dataloader库,使用相同的
userId
来取消重新获取作者。 但是,我想使用自定义缓存而不是dataloader
。
有没有办法阻止Post.author()
解析器并行执行?在Post.author()
解析器中,我想一次获取一个author
,检查其间的缓存以防止重复的http请求。
但是,现在从Post.author()
返回的promise已经排队并立即执行,因此我无法在每次请求之前检查缓存。
感谢您的任何提示!
答案 0 :(得分:0)
我绝对建议您查看DataLoader,因为它旨在解决此问题。如果你不直接使用它,至少你可以阅读它的实现(这不是那么多行)并借用你的自定义缓存上的技术。
GraphQL和graphql.js库本身并不关心加载数据 - 它们通过解析器函数将它们留给您。 Graphql.js只是急切地调用这些解析器函数,以便为查询提供最快的整体执行。你绝对可以决定返回顺序解析的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;
}
答案 1 :(得分:0)
仅对于将dataSource
与dataLoader
一起用于REST api的人(在这种情况下,它实际上并没有帮助,因为它是一个请求)。这是一个简单的缓存解决方案/示例。
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
进行缓存。
希望这会有所帮助。