Graphql 解析函数中的并行承诺执行

Graphql 解析函数中的并行承诺执行,graphql,graphql-js,apollo,Graphql,Graphql Js,Apollo,我有一个关于在GraphQL客户机的resolve函数中处理承诺的问题。传统上,解析程序将在服务器上实现,但我正在客户机上包装RESTAPI 背景和动机 给定解析程序,如: const resolvers = { Query: { posts: (obj, args, context) => { return fetch('/posts').then(res => res.json()); } }, Post: { author: (o

我有一个关于在GraphQL客户机的resolve函数中处理承诺的问题。传统上,解析程序将在服务器上实现,但我正在客户机上包装RESTAPI

背景和动机 给定解析程序,如:

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返回四个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()返回的每个承诺都将并行执行

我还能够使用facebook的库消除使用相同的
userId
重新获取作者的行为但是,我想使用自定义缓存而不是
数据加载器

问题 有没有办法阻止
Post.author()
resolver并行执行?在
Post.author()
resolver中,我想一次获取
author
s一个,检查中间的缓存以防止重复http请求

但是,现在从
Post.author()
返回的承诺会排队并立即执行,因此我无法在每次请求之前检查缓存


谢谢你的提示

我绝对推荐您查看,因为它的设计正是为了解决这个问题。如果不直接使用它,至少可以阅读它的实现(没有那么多行)并借用自定义缓存上的技术

GraphQL和GraphQL.js库本身并不关心加载数据——它们通过解析器函数将这一点留给您。js只是尽可能急切地调用这些解析器函数,以提供最快的查询整体执行速度。您完全可以决定返回按顺序解决的承诺(我不建议这样做),或者在DataLoader使用备忘录实现重复数据消除时返回承诺(这是您解决此问题的目的)

例如:

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;   
}

我绝对推荐你看看,因为它的设计就是为了解决这个问题。如果不直接使用它,至少可以阅读它的实现(没有那么多行)并借用自定义缓存上的技术

GraphQL和GraphQL.js库本身并不关心加载数据——它们通过解析器函数将这一点留给您。js只是尽可能急切地调用这些解析器函数,以提供最快的查询整体执行速度。您完全可以决定返回按顺序解决的承诺(我不建议这样做),或者在DataLoader使用备忘录实现重复数据消除时返回承诺(这是您解决此问题的目的)

例如:

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
dataLoader
一起使用restapi的人来说(在这种情况下,它并没有真正的帮助,因为它只是一个请求)。下面是一个简单的缓存解决方案/示例

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)
  }
}
注意:这里我使用
内存缓存
作为缓存机制。
希望这能有所帮助。

仅适用于使用
数据源
数据加载器
来处理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)
  }
}
注意:这里我使用
内存缓存
作为缓存机制。
希望这能有所帮助。

未来读者注意:我说的
graphql-js
解析器可以通过“并行”执行完成任何事情是错误的。正如Lee在下面所说的,解析函数及其返回的承诺都会急切地执行。在本例中,从
Post.author()
resolver返回的所有承诺都同时处于“飞行”状态,这使得我在上面尝试的缓存检查变得不可能。通过某个唯一的键(
id
url
)来记录请求是一种方法,我将使用DataLoader来实现这一点。请未来的读者注意:当我说
graphql js
解析器可以“并行”执行时,我错了。正如Lee在下面所说的,解析函数及其返回的承诺都会急切地执行。在本例中,从
Post.author()
resolver返回的所有承诺都同时处于“飞行”状态,这使得我在上面尝试的缓存检查变得不可能。通过一些唯一的键(
id
url
)来记忆请求是一种方法,我将使用DataLoader来实现这一点。