如何让async函数等待所有异步操作?解决函数提前返回问题
问题分析与解决方案
你的问题核心出在两个地方:一是**setAsyncTimeout没有正确等待异步回调完成**,二是混合使用await和.then()导致嵌套逻辑混乱,部分异步操作没被纳入等待链。这就导致putToDeath的Promise提前resolve,所以done会在setAsyncTimeout 463之前输出。
第一步:修复setAsyncTimeout函数
原来的setAsyncTimeout在执行回调后立刻resolve了Promise,但如果回调是async函数(里面有await),它并不会等待回调内部的异步操作完成。我们需要修改它,确保等待回调的异步逻辑执行完毕:
const setAsyncTimeout = (cb, timeout = 0) => new Promise(resolve => setTimeout(async () => { await cb(); // 等待异步回调中的所有操作完成 resolve(); }, timeout));
第二步:重构putToDeath,移除.then()嵌套
混合await和.then()很容易造成逻辑层级混乱,我们把所有.then()替换成await,让代码线性化,确保每一步都等待前一个异步操作完成:
async function putToDeath(conNum, exeNum) { let exeStartX = round(players[exeNum].x); let exeStartY = round(players[exeNum].y); console.log('walkTo 452'); await walkTo(conNum, window.innerWidth / 2, window.innerHeight / 2, 0.5); faceAudience(conNum, 'walk'); console.log('setAsyncTimeout 455'); await setAsyncTimeout(async () => { let exe = players[exeNum]; let direction = pointDirection(exe.x, exe.y, window.innerWidth / 2, window.innerHeight / 2); let distance = lineLength(exe.x, exe.y, window.innerWidth / 2, window.innerHeight / 2); let dest = getLineEndpoint(exe.x, exe.y, direction, distance - 50); console.log('walkTo 461'); await walkTo(exeNum, dest.x, dest.y, 0.75); console.log('setAsyncTimeout 463'); await setAsyncTimeout(async () => { swingSword(exeNum); await setAsyncTimeout(async () => { await die(conNum); await setAsyncTimeout(async () => { face(exeNum, 'walk', false); await setAsyncTimeout(async () => { await walkTo(exeNum, exeStartX, exeStartY, 0.75); face(exeNum, 'walk', true); // 如果这个方法是异步的,记得加上await }, 500); }, 500); }, 750); }, 500); }, 1000); }
为什么这样修改能解决问题?
- 修复后的
setAsyncTimeout:现在它会等待回调内部的所有异步操作完成后才resolve,确保每一个超时后的逻辑都执行完毕再进入下一步。 - 线性化的
await逻辑:移除.then()嵌套后,每一个await都会等待前一个异步操作完成,整个函数的Promise会一直等到最内层的face(exeNum, 'walk', true)执行完毕才会resolve。
这样当你执行putToDeath(0, 1).then(()=>{ console.log('done') })时,done会在所有操作(包括setAsyncTimeout 463之后的逻辑)完成后才输出。
内容的提问来源于stack exchange,提问作者Meler Lawler
相关产品推荐
相关产品推荐

