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

问题描述 投票:0回答:1

我正在使用 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]);

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

empty objects then filled after some time

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

这是我已经工作了几个月的副项目,我已经努力解决这个问题一周了,但无法弄清楚为什么以及如何解决这个问题。如果这恰好是您可能知道解决方案的问题,如果我能听到一些解释,我将不胜感激。

谢谢你

我正在尝试的是:循环数组并获取数组中每个元素的个人资料图片,然后使用对象将其存储到反应 useState 变量中。

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

javascript reactjs asynchronous async-await frontend
1个回答
0
投票

是和不是。事实上,

.forEach
内部的回调可以是异步的,但
forEach
语句本身不能。这意味着
console.log(avatarObject);
将在
forEach
语句内的回调仍在处理时执行。这就是为什么一开始你看到一个空物体,但一段时间后你看到物体被填满了。

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

1.将
Promise.all
map

一起使用

map
方法的工作方式与
forEach
方法类似,但不同之处在于它返回一个新数组,其中包含执行的每个回调的结果,在异步回调的情况下,结果将是Promise。鉴于此,您可以使用
Promise.all
语句等待
map
内的所有回调完成,然后再继续。就像下面这样:

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]);

请注意,这两种解决方案之间存在一些差异。如果你想更深入地了解这一点,我建议阅读这篇文章:for wait of VS Promise.all

© www.soinside.com 2019 - 2024. All rights reserved.