首页 > 解决方案 > 难以理解这个 Promise + async await 示例以在 JavaScript 中检索多个用户信息

问题描述

在 javascript 的 promise 和 async await 上关注本教程(https://javascript.info/fetch ),但无法理解它提供的练习。

问题是关于从 Github 检索多个用户信息。每个用户一个获取请求。请求不应该相互等待。以便数据尽快到达。

它提供的解决方案是

async function getUsers(names) {
  let jobs = [];

  for(let name of names) {
    let job = fetch(`https://api.github.com/users/${name}`).then(
      successResponse => {
        if (successResponse.status != 200) {
          return null;
        } else {
          return successResponse.json();
        }
      },
      failResponse => {
        return null;
      }
    );
    jobs.push(job);
  }

  let results = await Promise.all(jobs);

  return results;
}

我的第一个问题是,我们可以await用于获取吗?即以下片段是否等同于他提供的解决方案?

async function getUsers2(names) {
  let jobs = [];

  for(let name of names) {
    let response
    try {
      response = await fetch(`https://api.github.com/users/${name}`);
    } catch(e) {
      response = null
    }
    const job = response && response.json()
    jobs.push(job);
  }

  let results = await Promise.all(jobs);

  return results;
} 

此外,教程说

.then 调用直接附加到 fetch,因此当我们有响应时,它不会等待其他 fetch,而是立即开始读取 .json()。

如果我们使用 await Promise.all(names.map(name => fetch(...))),并在结果上调用 .json(),那么它将等待所有 fetch 响应。通过将 .json() 直接添加到每个 fetch,我们确保各个 fetch 开始以 JSON 格式读取数据,而无需相互等待。

他的意思是如果我们这样写解决方案

async function getUser(name) {
  const response = await fetch(`https://api.github.com/users/${name}`)

  return response.ok ? await response.json : null
}

async function getUsers(names) {
  return await Promise.all(names.map(name => getUser(name)))
}

我们将无法达到这样的效果,即我们不希望请求不应该互相等待?

标签: javascriptasynchronousecmascript-6async-awaitfrontend

解决方案


我的第一个问题是,我们可以使用 await 进行获取吗?即以下片段是否等同于他提供的解决方案?

不。当在异步函数的直接主体中时,只要有await,该函数将完全暂停,直到以下 Promise 解决。所以,循环

  for(let name of names) {
    let response
    try {
      response = await fetch(`https://api.github.com/users/${name}`);
    } catch(e) {
      response = null
    }

在继续初始化下一个请求之前,需要串行等待每个响应的标头被接收。

他的意思是如果我们这样写解决方案

首先需要调整语法:.json是一个方法,所以需要调用:

async function getUser(name) {
  const response = await fetch(`https://api.github.com/users/${name}`)
  return response.ok ? await response.json() : null
  //                                      ^^
}

但这完全没问题。函数中唯一awaitgetUsers就是等待整体 Promise.all解决;.map数组对调用的pinggetUser是同步执行的,因此所有请求都会立即发送出去,因此没有一个网络请求需要等待其他任何一个请求完成才能工作。

作者所指的问题是调用调用Promise.all数组fetch,而不是.json()调用数组:

// Bad, do not use:
const getUsers = async (names) => {
  const responses = await Promise.all(names.map(
    name => fetch(`https://api.github.com/users/${name}`)
  ));
  return Promise.all(responses.map(
    response => response.ok ? response.json() : null
  ));
}

上面的问题是脚本必须等待每个请求的所有响应标头都被接收,然后才能开始解析它们中的任何一个的响应主体。

另一个并行解决方案:

const getUsers = names => Promise.all(names.map(
  async (name) => {
    const res = await fetch(`https://api.github.com/users/${name}`);
    return res.ok ? res.json() : null;
  }
));

推荐阅读