【发布时间】:2022-01-23 06:42:33
【问题描述】:
我正在基于游戏循环在 nodejs 中编写应用程序。循环的每次迭代,它都会触发一个事件发射器,并像这样调用一个更新方法:
updateLoop() {
while (!this.windowShouldClose()) {
this.onUpdate.emit();
this.update();
}
}
似乎事件发射器可以帮助在游戏对象上编写异步函数,这些函数在不同操作之间等待帧,就像协程一样。我写了一个实用函数,它可以回调,或者在事件发射器的下一次发射时解决一个承诺:
nextFrame() {
return new Promise((resolve) => {
this.onUpdate.once(resolve); // event emitter will resolve promise on next emit()
})
}
nextFrameCallback(callback) {
this.onUpdate.once(callback);
}
// example use
async doThingsAsync() {
// do something
await this.nextFrame();
// do something else on the next frame / loop iteration
}
虽然基于回调的函数按预期工作,但 nextFrame() 的承诺版本并没有在我预期的时间解决承诺。在我的示例中,await nextFrame() 仅在 updateLoop() 的外循环退出后才解析。我在 promise 上附加了一个 console.log 以了解更多信息,并发现控制台日志和 resolve() 确实在循环中被调用,但等待它仍然会等待循环完全终止。
nextFrameDebug() {
return new Promise((resolve) => {
this.onUpdate.once(() => {
console.log('debug nextFrame'); // this prints during the expected loop iteration
resolve(); // this does not actually resolve until updateLoop() terminates
})
})
}
这是一个演示上述功能的 JSFiddle:https://jsfiddle.net/8L4wub29/5/
似乎我接近一个功能性解决方案,但我对承诺或异步函数有一些误解。这是否与从循环中调用异步函数有关?我如何编写 nextFrame() 以便承诺在循环的下一次迭代时解决,而不是在循环退出后解决?我意识到对于游戏中的大多数功能,毫秒超时更有用,但在某些情况下,例如等待物理更新,游戏可能只想等待一帧作为实用函数。基于回调的版本运行良好,但如果需要多次使用则需要嵌套,看起来不像使用await那么干净
【问题讨论】:
-
您是否有理由不将本机
async函数用于您的onUpdate函数?您应该只直接使用Promise来调整早于Promise的旧API(例如window.setTimeout)或用于存根/测试。 Promise 不应该用于表示可重复的事件。为此,请考虑改用RxJS(RxJS 与 ReactJS 完全无关,bte)。 -
我正在调用 node-addon-api C 库来在这个应用程序中进行图形/渲染,它的 API 将在自动完成绘图后等待一帧的剩余持续时间,我假设都是同步发生的。
-
"并且它的 API 会在自动绘制完成后等待一帧的剩余持续时间" - 呃,这听起来可怕(在 JS 中,什么都没有 i> 应该“等待”或阻塞线程)。您可能在滥用该库。请告诉我们更多关于你在做什么。
-
我仍在编写绑定,所以文档有点稀疏。 github.com/twuky/raylib-4.0 是它的仓库。据我所知,无论目标 FPS 发送到什么位置,BeginDrawing() 和 EndDrawing() 调用之间似乎总是占用一帧的持续时间。在我看来,您是在暗示主更新循环本身是一个异步函数,如果该循环只是更新游戏逻辑,那么绘制调用可能会在其他地方同步发生?
标签: javascript node.js asynchronous async-await promise