每次迭代中带有 async/await 的 array.forEach(callback) 是异步的吗?[复制]

Is array.forEach(callback) with async/await inside each iteration asynchronous? [duplicate]

提问人:Jay 提问时间:11/5/2023 更新时间:11/5/2023 访问量:49

问:

我正在使用 react,我有以下代码:

    const [avatarURLs, setAvatarURLs] = useState({});
    const [currentTutorProfile, setCurrentTutorProfile] = useState({});
    const [avatarURLsLoaded, setAvatarURLsLoaded] = useState(false); // New state

    useEffect(() => {
        const avatarObject = {};
        const fetchAvatar = async (tutorProfile) => {
            try {
                if (tutorProfile) {
                    const response = await axios.get(
                        `${process.env.REACT_APP_BASE_URL}/users/${tutorProfile.owner}/avatar`,
                        { responseType: "arraybuffer" }
                    );
                    const blob = new Blob([response.data], {
                        type: "image/png",
                    });
                    const imageUrl = URL.createObjectURL(blob);

                    avatarObject[tutorProfile.owner] = "imageUrl";
                }
            } catch (error) {
                // console.error("Error fetching user avatar:", error);
            }
        };
        tutorProfilesArray.forEach(fetchAvatar);
        console.log(avatarObject); // THIS ONE

        setAvatarURLs(avatarObject);
        setAvatarURLsLoaded(true); // Mark that avatarURLs are loaded
    }, [tutorProfilesArray]);

我有一个控制台.log(avatarObject) // 这个,测试avatarObject的行,我得到空对象,然后对象值在一段时间后被填充。

空对象,然后在一段时间后填充

我注意到只有当 api 调用与 await 一起使用时才会出现这种行为(例如:await axios.get())。当在没有 await 的情况下使用 api 调用时(例如:axios.get()),avatarObject 会立即更新。所以这是我不明白的;既然使用了 await,那么即使完成了 api 调用,该行也不应该以顺序(同步)方式执行吗?

这是我几个月来一直在做的副业,我一直在努力解决这个问题一个星期,但无法弄清楚为什么以及如何解决这个问题。如果这恰好是您可能知道解决方案的事情,如果我能听到一些解释,我将不胜感激。

谢谢

我正在尝试的是:遍历一个数组并为数组的每个元素获取个人资料图片,并使用对象将其存储到 react useState 变量中。

问题:它确实将个人资料图片存储到 useState 变量中,但是,它是在一段时间后完成的(我假设),因此当前端最初呈现时,它看不到任何个人资料图片,因此它没有呈现个人资料图片。

JavaScript ReactJS 异步 async-await 前端

评论

1赞 Jaromanda X 11/5/2023
array.forEach使用异步函数很少做你真正想要的 - 除非你想要的是回调函数在后续代码中的已知点绝对没有可用的结果......即是的,异步函数即使在.forEach
0赞 Bergi 11/5/2023
"当在没有 await 的情况下使用 api 调用时(例如:axios.get()),avatarObject 会立即更新“ - 当然,但它会用错误的值更新,因为本来是一个 promise 并且未定义。responseresponse.data
0赞 Jay 11/6/2023
@JaromandaX我明白了,我会记住继续写 JS,谢谢!
0赞 Jay 11/6/2023
@Bergi 你是对的,这就解释了为什么它“看起来”像是在没有等待的情况下立即更新的。谢谢。

答:

0赞 Hugo Sangaletti 11/5/2023 #1

是的,也不是。实际上,里面的回调可以是异步的,但语句本身不能。这意味着 将在语句中的回调仍在处理时执行。这就是为什么一开始你会看到一个空对象,但一段时间后你会看到对象被填满的原因。.forEachforEachconsole.log(avatarObject);forEach

有一些方法可以解决此问题:

1. 与Promise.allmap

该方法的工作方式与方法类似,但区别在于它返回一个新数组,其中包含执行的每个回调的结果,如果是异步回调,则结果将是 Promise。鉴于此,您可以使用该语句等待 中的所有回调完成,然后再继续。如下所示:mapforEachPromise.allmap

useEffect(() => {
  async function fetchUsersAvatarAndUpdateState() {
    const avatarObject = {};

    await Promise.all(
      tutorProfilesArray.map(async (tutorProfile) => {
        // ... code
      }),
    );
    console.log(avatarObject); // THIS ONE

    setAvatarURLs(avatarObject);
    setAvatarURLsLoaded(true); // Mark that avatarURLs are loaded
  }

  fetchUsersAvatarAndUpdateState();
}, [tutorProfilesArray]);

2.改用for await

该循环是异步的,可用于异步迭代,如下所示:for await (...)

useEffect(() => {
  async function fetchUsersAvatarAndUpdateState() {
    const avatarObject = {};

    for await (const tutorProfile of tutorProfilesArray) {
      // ... code
    }
    console.log(avatarObject); // THIS ONE

    setAvatarURLs(avatarObject);
    setAvatarURLsLoaded(true); // Mark that avatarURLs are loaded
  }

  fetchUsersAvatarAndUpdateState();
}, [tutorProfilesArray]);

请注意,这两种解决方案之间存在一些差异。如果你想更深入地了解这一点,我建议阅读这个线程:等待VS Promise.all

评论

1赞 pilchard 11/5/2023
被长期存在的副本覆盖