【问题标题】:fetch result from API returns undefined从 API 获取结果返回未定义
【发布时间】:2021-04-26 13:26:39
【问题描述】:

我正在尝试从 API 获取数据,但结果一直未定义。我正在使用 promise.all 链接 API 调用,然后链接 promise 以对数据执行其他操作。我似乎无法将我的结果传递给 state。

这是有问题的代码:

if (gameDifficulty === "mixed") {
  const result = await Promise.all([
    fetchClient.getData(
      getEndpoint(
        `amount=${countValues[0]}&difficulty=easy&type=${questionType}`
      )
    ),
    fetchClient.getData(
      getEndpoint(
        `amount=${countValues[1]}&difficulty=medium&type=${questionType}`
      )
    ),
    fetchClient.getData(
      getEndpoint(
        `amount=${countValues[2]}&difficulty=hard&type=${questionType}`
      )
    ),
  ])
    .then(function (responses) {
      console.log(responses);
      console.log(result); //returns undefined
      return Promise.all(
        responses.map(function (response, idx) {
          return fetchClient.processData(response, "results");
        })
      );
    })
    .then(function (data) {
      console.log(data);

      console.log(result); // returns undefined
      return [...data[0], ...data[1], ...data[2]];
    });
}

【问题讨论】:

  • 您将async/await.then 代码混合在一起,不要那样做。只需使用其中之一。还有为什么你甚至需要result 在那个.then,你已经有responses 这将是同一件事 - API 请求的响应
  • 这是一个跟踪问题的尝试。我想看看会得到什么。其他控制台日志返回预期结果,但整个函数返回未定义。我认为这是一个异步的事情。

标签: javascript reactjs fetch undefined promise.all


【解决方案1】:

使用async/await 语法使其变得简单。

避免将async/awaitthen/catch 风格混用。

const getDataResponses = await Promise.all([
  fetchClient.getData(
    getEndpoint(
      `amount=${countValues[0]}&difficulty=easy&type=${questionType}`
    )
  ),
  fetchClient.getData(
    getEndpoint(
      `amount=${countValues[1]}&difficulty=medium&type=${questionType}`
    )
  ),
  fetchClient.getData(
    getEndpoint(
      `amount=${countValues[2]}&difficulty=hard&type=${questionType}`
    )
  ),
]);

const data = await Promise.all(
  getDataResponse.map((response) => {
    return fetchClient.processData(response, "results");
  })
);

console.log('getDataResponses', getDataResponses);
console.log('processDataResponse', data);
return [...data[0], ...data[1], ...data[2]];

【讨论】:

  • 非常感谢。我尝试了这个并得到了相同的结果,但至少它的格式和编写更好,所以这很棒。 processDataResponse 控制台日志返回一个 promise 。似乎它没有被传递到最后一行。
  • @EkeEke 感谢您的快速回复,我更新了我的答案,在 const data = Promise.all 处缺少 await 关键字 => const data = await Promise.all
  • 成功了!非常感谢。这暴露了我对异步性缺乏了解。不过我很好奇,为什么将 async/await 与 then/catch 语法配对是不可取的?它会导致奇怪的边缘情况还是问题主要来自人为错误?
  • 它需要深入了解js中的异步过程。当你确定你需要它们时,它只会混合它们。
【解决方案2】:

在您的浏览器中转到您的网络选项卡发送一个 api 请求,看看它是否正在发送任何飞行前请求?

飞行前请求将具有 OPTIONS 方法,以便您识别它

【讨论】:

  • 我似乎找不到 OPTIONS 部分,但我的控制台日志会返回调用中的值,如果这有帮助的话。我检查了网络标签。
  • 您将在网络选项卡中查看所有 api 调用和 api 调用的结果,所以去那里检查您的 api 是否返回未定义
  • 哦,对了,实际的 API 调用会返回它们的值,这是链式 promise 没有按预期工作。代码在没有结果和中断的情况下继续运行。
【解决方案3】:

由于result 直接从await 上的Promise 获取值,因此您可以使用它来代替.then()

const responses = await Promise.all([
    fetchClient.getData(
        getEndpoint(
            `amount=${countValues[0]}&difficulty=easy&type=${questionType}`
        )
    ),
    fetchClient.getData(
        getEndpoint(
            `amount=${countValues[1]}&difficulty=medium&type=${questionType}`
        )
    ),
    fetchClient.getData(
        getEndpoint(
            `amount=${countValues[2]}&difficulty=hard&type=${questionType}`
        )
    ),
])

console.log('responses', responses);

const data = await Promise.all(
    responses.map(function (response, idx) {
        return fetchClient.processData(response, "results");
    })
);

console.log('data', data);
console.log([...data[0], ...data[1], ...data[2]]);

【讨论】:

  • 我已经这样做了 - @hoangdv 建议与上述相同,并且已经解决了这个问题。非常感谢。我没有足够高的声誉来反映我的支持,但我已标记为有用。再次感谢。
【解决方案4】:

其他答案显示了如何更正代码,同时避免混淆承诺链和 await 运算符的使用。这个答案是关于为什么会出现 result 的未定义值:

 const result = await <promise chain>

在执行await 操作数之前为result 创建一个绑定。像所有简单的变量定义一样,它以 undefined 的值开始,并且由于它现在存在,因此在以后的代码中访问 result 不会产生错误。

promise 链的对象值是链中调用的最后一个 promise 方法(即thencatchfinally)返回的promise。这是await 操作数在将其(成功)值作为await 表达式的值返回之前等待解决的承诺。

因此await 直到最后一个then 处理程序返回之后才返回要分配给result 的值,这意味着在匿名函数之后

.then(function (data) {
  console.log(data);

  console.log(result); // returns undefined
  return [...data[0], ...data[1], ...data[2]];
});

已完成执行并返回。在那之前,所有console.log(result) 调用记录undefined,因为没有分配给result


TLDR;

附加到 Promise 的处理程序在它们附加到的 Promise 被履行或拒绝后,在事件循环的新调用中异步执行。

await 是一元运算符,能够保存执行状态并返回事件循环,直到其承诺操作数被确定。

贴出代码的操作顺序是

  1. 定义result,初始化为undefined,并设置await操作符,获取第5步then返回的promise结果。

  2. 发出三个端点数据请求。

  3. 使用Promise.all 从步骤 2 中获取一组响应。

  4. (然后)使用Promise.all从步骤3的结果中异步获取数组中每个响应的过程数据数组。

  5. (然后)展平第 4 步中的数据数组并返回展平后的数组

  6. (然后)await 运算符获取带有第 5 步结果的回调,并将其作为 await 表达式的值返回

  7. 从第 6 步返回的值用于初始化 const result 声明。从语法上讲,这发生在 const result 语句结束之前,但实际上已经分散到多个网络请求和事件循环调用所需的时间。

【讨论】:

  • 那些console.logs 是寻找问题所在的不幸尝试。感谢您的详细解释。我仍然很想知道为什么我编写的公认笨重的代码在运行后总是返回 undefined,这样如果我下次看到它,我可以更好地进行故障排除。对此有什么想法吗?非常感谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-07-11
  • 2018-07-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-19
相关资源
最近更新 更多