我想实现一个异步任务调度。这是我的代码:
let gameTick = 0;
const tasks: Record<string, Function[]> = {};
function sleep(t: number) {
const targetTick = gameTick + t;
if (!tasks[targetTick]) {
tasks[targetTick] = [];
}
return new Promise<void>(resolve => {
tasks[targetTick].push(resolve);
});
}
async function task1() {
print('start task1');
for (let i = 0; i < 3; i++) {
print(`execute: ${i}`);
await sleep(2);
}
}
// task1();
for (gameTick = 0; gameTick < 10; gameTick++) {
print(`tick: ${gameTick}`);
tasks[gameTick]?.forEach(f => f())
if (gameTick == 2) task1();
}
这段代码在 TypeScriptToLua 环境和 node.js 环境中的行为不同,这让我很困惑。
TypeScriptToLua | nodejs |
---|---|
勾选:0 | 勾选:0 |
勾选:1 | 勾选:1 |
勾选:2 | 勾选:2 |
开始任务1 | 开始任务1 |
执行:0 | 执行:0 |
勾选:3 | 勾选:3 |
打勾:4 | 打勾:4 |
执行:1 | 打勾:5 |
打勾:5 | 打勾:6 |
打勾:6 | 打勾:7 |
执行:2 | 打勾:8 |
打勾:7 | 打勾:9 |
打勾:8 | 执行:1 |
打勾:9 |
我能理解TypeScriptToLua的流程,相当于resolve后恢复协程,await后立即执行代码。但我不懂 NodeJ。为什么循环结束后还继续执行,而且只执行一次?
问题在于 Promise 是异步的,但你的游戏循环是同步的。当您调用
resolve()
时,不会直接继续执行协程。它仅解析 Promise,从而安排任何已注册的 Promise 反应(例如,await
该 Promise 的异步代码的延续,或通过 .then()
添加的回调函数)在下一个微任务中尽快运行。因此,在运行下一个游戏之前,您需要给它一些时间来完成此操作:
(tasks[2] ??= []).push(task1);
for (gameTick = 0; gameTick < 10; gameTick++) {
print(`tick: ${gameTick}`);
tasks[gameTick]?.forEach(f => f())
await Promise.resolve(); // if not even setTimeout()
}