这是因为 forEach
方法不会等待 async/await
异步操作的完成。forEach
方法是一种同步的方法,它会在每个迭代内部同步执行一个回调函数。当遇到 await
时,会立即暂停执行,但是 forEach
方法不会等待回调函数中的 await
异步操作完成,而是立即执行下一个循环。
举个例子,假如在 React Hook 组件中,我们想要通过遍历异步请求一个数组中的数据:
jsx
import React, { useState, useEffect } from 'react';
function MyComponent() {
const [data, setData] = useState([]);
useEffect(() => {
const fetchData = async () => {
const result = [];
[1, 2, 3].forEach(async (num) => {
const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${num}`);
const data = await response.json();
result.push(data);
})
setData(result);
}
fetchData();
}, []);
return (
<ul>
{data.map(item => <li key={item.id}>{item.title}</li>)}
</ul>
);
};
export default MyComponent;
在这个例子中,使用 forEach
方法来遍历并异步请求数据,然后将请求到的数据添加到 result
数组中。最后,将 result
数组通过 setData
设置为 data
的状态。然而,这个代码并不能达到我们的预期。
原因是 forEach
方法是同步执行的,在执行的过程中会立即调用回调函数。在回调函数中使用 async/await
将请求转为异步操作,当执行到 await
时,虽然会暂停当前的执行,但是 forEach
会继续执行下一次循环,因此 result
数组没有等待所有的异步请求完成后再被更新,而是由于异步请求还没有完成,因此我们只得到了一个空的数组。
解决这个问题的方法是使用 for..of
循环或者 Promise.all
。在这里,我提供一个使用 Promise.all
的解决方案:
Promise.all的写法
jsx
import React, { useState, useEffect } from 'react';
function MyComponent() {
const [data, setData] = useState([]);
useEffect(() => {
const fetchData = async () => {
const promises = [1, 2, 3].map(async (num) => {
const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${num}`);
return response.json();
});
const result = await Promise.all(promises);
setData(result);
}
fetchData();
}, []);
return (
<ul>
{data.map(item => <li key={item.id}>{item.title}</li>)}
</ul>
);
};
export default MyComponent;
在这个例子中,我们将 forEach
方法替换为 map
,用一个数组来保存返回的 Promise。然后,使用 Promise.all
来并行地等待所有的 Promise 被解决,并将它们的值传递给一个数组,然后使用 setData()
方法来更新组件状态。这样就可以达到我们的预期:异步请求完成后更新组件状态。
for...of的写法
使用 for...of
的方法和 forEach
的方法类似,也是使用异步函数进行请求。但是,在 for...of
循环体内部可以使用 await
等待异步操作的完成。例如:
jsx
import React, { useState, useEffect } from 'react';
function MyComponent() {
const [data, setData] = useState([]);
useEffect(() => {
const fetchData = async () => {
const result = [];
for (const num of [1, 2, 3]) {
const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${num}`);
const data = await response.json();
result.push(data);
}
setData(result);
}
fetchData();
}, []);
return (
<ul>
{data.map(item => <li key={item.id}>{item.title}</li>)}
</ul>
);
};
export default MyComponent;
在这个例子中,我们使用了 for...of
循环来遍历数组。在循环体内部使用 await
等待异步操作,这样可以保证每个异步操作在上一个异步操作完成后才会被执行。
通过使用 for...of
来遍历数组,可以很好地保证每个异步操作能够按顺序被执行,并且可以等待每个异步操作的完成。这是相对于 forEach
来说的一种更为简洁的方式。