异步等待不等待 useEffect 内的响应

Posted

技术标签:

【中文标题】异步等待不等待 useEffect 内的响应【英文标题】:Async await is not waiting for response inside useEffect 【发布时间】:2020-12-03 11:42:43 【问题描述】:

我正在为 Hacker news 创建简单的克隆,我想使用 useEffect 并在其中返回 id,然后使用这些 id 获取前 50 个帖子。

问题在于fetchHackerNewsPosts 中的第二个函数,它不会等待它在postsArray 中添加数据,而是将其打印为空。

import React from "react";
import logo from "./logo.svg";
import "./App.css";

function App() 
  const [posts, setPosts] = React.useState([]);

  React.useEffect(() => 
    let postsArray = [];

    async function fetchHackerNewsPosts() 
      try 
        // it waits this function
        let postsIDsArray = await fetch(
          "https://hacker-news.firebaseio.com/v0/topstories.json"
        )
          .then((res) => res.json())
          .then((result) => 
            return result;
          );

        // it doesn't wait this function
        await postsIDsArray.slice(0, 50).map((postId) => 
          fetch(`https://hacker-news.firebaseio.com/v0/item/$postId.json`)
            .then((res) => res.json())
            .then((result) => 
              postsArray.push(result);
            );
        );
       catch (error) 
        console.log(error);
      
    

    fetchHackerNewsPosts();
    console.log(postsArray);
  , []);

  return (
    <div className="p-10">
      <header className="">
        <h1> Hacker news API </h1>
      </header>
      <div className="bg-blue-600">list</div>
    </div>
  );


export default App;

【问题讨论】:

这将对您有所帮助:async-await-inside-arraymap 【参考方案1】:

我认为这里有两个问题。

1 - 您正在等待的函数不是异步函数。您正在发布postsIDsArray.slice(0, 50).map(...) - 它只会解析为一个空数组(您不会返回获取)。你可以返回 fetch,然后你会得到一个 promise 数组,然后你可以通过 Promise.all 等待:

await Promise.all(postsIDsArray.slice(0, 50).map((postId) => fetch(`https://hacker-news.firebaseio.com/v0/item/$postId.json`)
  .then((res) => res.json())
  .then((result) => 
    postsArray.push(result);
 ));

这仍然会在您 console.log(postsArray); 的位置记录一个空数组 - 因为您没有等待 fetchHackerNewsPosts。

可能值得考虑使用全部等待/异步样式或全部承诺样式,而不是将两者混合使用。 ~~然而,这通常意味着切换到常规循环而不是 map/forEach,所以你可以在两者之间等待。~~ - 编辑 - 删除,欢呼莱昂

React.useEffect(() => 
    async function fetchHackerNewsPosts() 
      const posts = [];

      try 
        const postIDsResponse = await fetch(
          "https://hacker-news.firebaseio.com/v0/topstories.json"
        );
        
        const postIDs = await postIDsResponse.json();

        for (const postID of postIDs) 
          const postResponse = await fetch(`https://hacker-news.firebaseio.com/v0/item/$postId.json`);
          const post = await post.json();
          posts.push(post);
        

        return posts;
       catch (error) 
        console.log(error);
      
    

    setPosts(await fetchHackerNewsPosts());
  , []);

正如所指出的 - Promise.all 实际上在这里可能会快很多,因为您无需等待前一个请求完成后再触发下一个请求。

React.useEffect(() => 
    async function fetchHackerNewsPosts() 
      try 
        const postIDsResponse = await fetch(
          "https://hacker-news.firebaseio.com/v0/topstories.json"
        );
        
        const postIDs = await postIDsResponse.json();

        const postRequests = postIDs.map(async postID => 
          const postResponse = await fetch(`https://hacker-news.firebaseio.com/v0/item/$postId.json`);
          return await post.json();
        );
        
        return await Promise.all(postRequests);
       catch (error) 
        console.log(error);
      
    

    const posts = await fetchHackerNewsPosts();
    setPosts(posts);
  , []);

【讨论】:

如果有很多帖子,for (const postID of postIDs) 将比Promise.all 花费更长的时间。但是Promise.all 也不限制你链接.thens:Promise.all(ids.map(async id =&gt; /* await stuff here then return when done */ )) @LionelRowe - 干杯,没想到!更新了答案以包含一个 promise.all 版本

以上是关于异步等待不等待 useEffect 内的响应的主要内容,如果未能解决你的问题,请参考以下文章

在 useEffect 挂钩中取消所有异步/等待任务以防止反应中的内存泄漏的正确方法是啥?

Java 不等待异步调用响应

如何告诉 useEffect 等待数据 API?

泽西客户端异步 POST 请求不等待响应

如何在节点 js 中正确使用等待/异步与 for 循环

React/React Context API:使用 useEffect() 钩子时等待上下文值