在 JavaScript 中,如果你想要在循环中执行异步操作,但又希望这些异步操作能够按顺序执行(而不是同时执行,从而导致可能的竞态条件),你可以使用几种不同的方法来实现这一点。下面是一些常用的方法:

# 1. 使用 async/awaitfor...of 循环

你可以使用 async/await 结合 for...of 循环来顺序执行异步操作。这种方法可以让代码更清晰,易于理解。

async function fetchData() {
    const urls = ['url1', 'url2', 'url3'];
    for (const url of urls) {
        const response = await fetch(url);
        const data = await response.json();
        console.log(data);
    }
}
 
fetchData();

# 2. 使用 Promise.then()

你可以在循环中逐个创建 Promise,并将它们链接起来。这种方法类似于回调地狱,但在现代 JavaScript 中通常不推荐使用,因为它降低了代码的可读性和可维护性。

function fetchData(url) {
    return fetch(url).then(response => response.json());
}
 
const urls = ['url1', 'url2', 'url3'];
let promiseChain = Promise.resolve(); // 初始化为已解决的 Promise
urls.forEach(url => {
    promiseChain = promiseChain.then(() => fetchData(url)).then(data => console.log(data));
});

# 3. 使用 async/awaitreduce() 方法

你可以使用 Array.prototype.reduce() 方法来累积异步操作的结果,并结合 async/await 来按顺序执行它们。

async function fetchAllData(urls) {
    return urls.reduce(async (sequence, url) => {
        await sequence; // 等待前一个 Promise 完成
        const response = await fetch(url);
        const data = await response.json();
        console.log(data);
        return Promise.resolve(); // 返回一个新的已解决的 Promise,以便链式调用下一个 URL
    }, Promise.resolve()); // 初始值为已解决的 Promise
}
 
const urls = ['url1', 'url2', 'url3'];
fetchAllData(urls);

# 4. 使用 async/awaitfor 循环的索引(不推荐)

虽然不推荐直接在循环中使用索引来处理异步操作(因为这可能导致竞态条件),但你可以这样做:

async function fetchAllData(urls) {
    for (let i = 0; i < urls.length; i++) {
        const response = await fetch(urls[i]);
        const data = await response.json();
        console.log(data);
    }
}
 
const urls = ['url1', 'url2', 'url3'];
fetchAllData(urls);

# 最佳实践:使用 async/awaitfor...offor 循环(推荐)

通常,使用 async/awaitfor...of 或简单的 for 循环是处理顺序异步操作的最好方法,因为它既清晰又易于维护。避免使用过时的 Promise 链方法,除非你有特定的理由(例如,需要处理错误而不中断整个链)。对于现代 JavaScript 项目,推荐使用第一种或第三种方法。