如何多次调用同一JS函数并等待上一次执行完成后再运行?
问题原因
你当前的写法无法顺序执行的核心原因是for循环是同步运行的,setTimeout属于异步宏任务,不会阻塞循环推进,代码运行时会瞬间触发所有rollSkins(你循环中写的rollDice属于命名笔误)调用,所有延时任务几乎同时启动,自然达不到“上一次执行完再跑下一次”的效果。
之前你用await没生效,基本是两个原因:一是被等待的函数没有返回Promise,await对普通同步函数、没做Promise包装的异步回调函数不会有等待效果;二是await没有写在async标记的函数内部,不符合语法规则。
正确实现方案
第一步:封装通用延时工具
把setTimeout包装成Promise形式,方便用await等待延时结束:
const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms));
第二步:改造原有业务函数
给原有包含异步逻辑的函数加上async标记,内部用await等待延时和子调用执行完成,确保函数执行结束时,内部所有异步逻辑已经跑完:
async function rollSkins(results, rollN) { // 此处放原rollSkins函数里setTimeout之前的同步逻辑 // 等待原逻辑里的500ms延时 await wait(500); // 等待roll函数全流程执行完成 await roll(results, rollN); } async function roll(results, rollN) { // 此处放原roll函数里setTimeout之前的同步逻辑 // 等待原逻辑里的8500ms延时 await wait(8500); // 此处放原roll函数setTimeout回调里的收尾逻辑 }
第三步:改造循环逻辑
把循环放在async函数内部,每次循环用await等待当前调用全流程结束后,再进入下一次循环:
// 用自执行async函数包裹业务逻辑,如果你已有外层async业务函数可以直接用 (async () => { // 注意循环变量用let声明,避免var带来的闭包变量污染问题 for (let x = 0; x < rollsArr.length; x++) { await rollSkins(data.results, x); // 如果需要单次执行完成后额外等待数秒再启动下一次,在这里加等待即可,比如额外等2秒: // await wait(2000); } // 所有轮次执行完成后的后续逻辑写在这里 })();
注意事项
- 不要用回调嵌套的方式实现顺序执行,轮次多了会形成回调地狱,维护成本极高
- 循环变量必须用
let声明,如果你用var,所有异步逻辑拿到的x都会是循环结束后的最终值,导致索引错误 - 所有需要等待异步结果的函数,都要确保返回Promise,否则
await不会按预期等待
内容的提问来源于stack exchange,提问作者Erik
相关产品推荐
相关产品推荐

