如何不使用async/await在循环中实现带延迟的Promise串行执行
问题描述
我编写了一个名为printLetters()的函数,预期实现间隔1000ms逐个打印字母的效果,但实际运行时所有字母会在1000ms后同时打印输出。将该函数改写为async异步函数后可正常实现逐字延迟打印的效果,请问如何在不使用async/await语法的前提下,让printLetters()在循环中按延迟逐个resolve Promise,实现间隔打印的预期效果?
问题复现代码
const printLetters = (l) => { for (let i = 0; i < l.length; i++) { delay(1000).then(() => { console.log(l[i]); }) } } const printLettersAsync = async(l) => { for (let i = 0; i < l.length; i++) { await delay(1000); console.log(l[i]); } } const delay = (t) => { return new Promise(res => setTimeout(res, t)) } const arr = ['a','b','c'] printLetters(arr) printLettersAsync(arr)
问题原因
原始printLetters()不符合预期的核心逻辑问题:
for循环是同步执行的,会在极短时间内完成所有迭代,一次性注册3个等待1000ms的延时任务- 3个定时器几乎同时启动,1000ms到期后同步触发回调,就会一次性输出所有字母
async/await版本能正常工作,本质是每次循环都会等待上一个延时Promise执行完成,才会启动下一个定时器,天然保证了执行顺序
无async/await的实现方案
不需要使用async/await语法,只需要通过Promise链式调用,保证下一次延时在上一次打印完成后再启动即可,有两种通用实现:
方案1:循环维护Promise链
初始化一个立即完成的Promise作为链头,每次循环都把下一轮的延时、打印逻辑挂到上一个Promise的.then()回调中,保证顺序执行:
const printLetters = (l) => { // 初始化立即resolve的Promise作为执行链起点 let taskChain = Promise.resolve(); for (let i = 0; i < l.length; i++) { taskChain = taskChain .then(() => delay(1000)) .then(() => { console.log(l[i]); }) } return taskChain; }
方案2:递归顺序执行
用递归替代循环,每次打印完当前位置的字母后,再递归调用函数处理下一个索引的内容,终止条件为所有字母打印完成:
const printLetters = (l, currentIndex = 0) => { // 递归终止:所有字符处理完成 if (currentIndex >= l.length) return Promise.resolve(); return delay(1000) .then(() => { console.log(l[currentIndex]); // 处理下一个字符 return printLetters(l, currentIndex + 1); }) }
以上两种实现的运行效果和async/await版本完全一致:首字母在1000ms后打印,之后每间隔1000ms输出下一个字母,不会出现同时打印的问题,调用方式和原函数完全一致,直接执行
printLetters(arr)即可。
内容的提问来源于stack exchange,提问作者D Park
相关产品推荐
相关产品推荐

