如何在JavaScript中同步打印Promise结果?循环执行顺序问题求解
解决Promise与for循环的异步顺序问题
嗨,我来帮你搞定这个问题!你的代码里有两个核心问题导致了奇怪的输出:
问题分析
变量
i的作用域问题:你没给i加let/const声明,它变成了全局变量。for循环是同步执行的,而getRandomWord()的then回调是异步触发的(因为内部有setTimeout)。等所有回调执行时,循环早就跑完了,i已经变成11,所以所有异步输出的都是11: xxx。await的使用限制:await只能在被async标记的函数内部使用,直接在for循环里用肯定会报错,所以得把循环逻辑放到一个async函数里。
解决方案(推荐:保证严格顺序执行)
如果你希望输出严格按照1到10的顺序来(处理完第1个再处理第2个),用async/await是最清晰的方式,而且完全不用修改getRandomWord函数:
const randomWords = require('random-words'); function getRandomWord() { return new Promise((resolve) => { setTimeout( () => resolve(randomWords()) ); }); } // 把循环逻辑包裹在async函数里 async function executeLoop() { // 用let声明i,形成块级作用域,每个迭代的i都是独立的 for (let i = 1; i <= 10; i++) { if (i % 3 === 0) { console.log(`${i}: divided`); } else { // await会等待Promise解析完成,再继续下一次循环 const randomWord = await getRandomWord(); console.log(`${i}: ${randomWord}`); } } } // 调用这个async函数启动逻辑 executeLoop();
这样修改后,输出会严格按照1到10的顺序,每个非3倍数的随机单词都会在对应的数字位置输出,再也不会出现11的情况。
另一种方案(并行异步,顺序不保证)
如果你不在乎非3倍数的输出顺序,只是想让每个回调能拿到正确的i,只需要把循环里的i改成let声明就行(let在for循环的每次迭代都会创建一个新的绑定):
const randomWords = require('random-words'); function getRandomWord() { return new Promise((resolve) => { setTimeout( () => resolve(randomWords()) ); }); } // 用let声明i,每个迭代的i都是独立的 for (let i = 1; i <= 10; i++) { if (i % 3 === 0) { console.log(`${i}: divided`); } else { getRandomWord().then(result => { console.log(`${i}: ${result}`); }); } }
不过这个方式下,所有非3倍数的getRandomWord请求会同时发起,它们的输出顺序可能和循环顺序不一致(取决于setTimeout的执行时机),如果需要严格顺序,还是推荐第一种async/await的写法。
内容的提问来源于stack exchange,提问作者David Johns
相关产品推荐
相关产品推荐

