Async函数执行不符合预期:循环重复触发问题排查与解决
Async函数执行顺序异常问题排查与解决
问题描述
使用Async函数时遇到执行顺序异常,数组遍历未按i=0→i=1→i=2…的预期顺序执行,反而出现i=0→i=1→i=0的重复执行情况。相关代码如下:
async function main() { async function func() { for (let i = 0; i < array.length; i++) { var result_amount = await calc(array[i]); if (result_amount > amount_start * 0.5) { console.log( "i: ", i, "Route: ", array[i][6], "amount_start: ", amount_start, "amount: ", result_amount ); } } } const POLLING_INTERVAL = process.env.POLLING_INTERVAL || 1000; Monitor = setInterval(async () => { await func(); }, POLLING_INTERVAL); } main().catch((error) => { console.error(error); process.exit(1); });
问题原因
核心冲突在于setInterval的定时机制与异步函数的执行逻辑:
setInterval会严格按照设定的间隔触发回调,不会等待上一次回调中的异步操作完成。- 如果
func的执行时间(遍历数组+多次await calc的总耗时)超过了POLLING_INTERVAL,下一次setInterval的回调就会在上一次func还未执行完毕时启动,导致多个func实例同时运行。不同实例的数组遍历会交叉执行,最终出现i顺序混乱、重复的现象。
解决方案
方案一:用递归setTimeout替代setInterval
通过递归调用setTimeout,确保上一次func完全执行完成后,再调度下一次执行,从根源避免重叠执行:
async function main() { async function func() { for (let i = 0; i < array.length; i++) { var result_amount = await calc(array[i]); if (result_amount > amount_start * 0.5) { console.log( "i: ", i, "Route: ", array[i][6], "amount_start: ", amount_start, "amount: ", result_amount ); } } } const POLLING_INTERVAL = process.env.POLLING_INTERVAL || 1000; // 递归调度,确保前一次执行完成后再启动下一次 async function monitorLoop() { await func(); setTimeout(monitorLoop, POLLING_INTERVAL); } monitorLoop(); } main().catch((error) => { console.error(error); process.exit(1); });
方案二:添加执行锁控制并发
如果需要保留setInterval,可以通过一个布尔变量作为执行锁,确保同一时间只有一个func实例在运行:
async function main() { let isRunning = false; // 执行锁,标记当前是否有func在运行 async function func() { for (let i = 0; i < array.length; i++) { var result_amount = await calc(array[i]); if (result_amount > amount_start * 0.5) { console.log( "i: ", i, "Route: ", array[i][6], "amount_start: ", amount_start, "amount: ", result_amount ); } } } const POLLING_INTERVAL = process.env.POLLING_INTERVAL || 1000; Monitor = setInterval(async () => { if (isRunning) return; // 若已有执行中的func,跳过本次回调 isRunning = true; try { await func(); } finally { isRunning = false; // 执行完成后释放锁,允许下一次执行 } }, POLLING_INTERVAL); } main().catch((error) => { console.error(error); process.exit(1); });
内容的提问来源于stack exchange,提问作者Dani S
相关产品推荐
相关产品推荐

