拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 Promiseresolve()被呼叫但直到它被呼叫的回圈终止时才会决议

Promiseresolve()被呼叫但直到它被呼叫的回圈终止时才会决议

白鹭 - 2022-01-25 2001 0 0

我正在基于游戏回圈在 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()退出后才会决议我将 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/ 似乎我已经接近功能解决方案了,但是我对 promise 或异步函式有一些误解。这是否与从回圈内呼叫异步函式有关?我如何撰写 nextFrame() 以便承诺在回圈的下一次迭代中解决,而不是在回圈退出之后?我意识到对于游戏中的大多数功能,以毫秒为单位的超时更有用,但在某些情况下,例如等待物理更新,游戏可能只想等待一帧作为效用函式。基于回呼的版本作业正常,但如果你需要多次使用它那么你需要嵌套它,这看起来不像使用那么干凈await

uj5u.com热心网友回复:

Promise 保证它们的.then回呼将始终被异步呼叫。这意味着如果你有一些随机的承诺,你会做这样的事情:

somePromise.then(() => {
  console.log('inside');
});
console.log('outside');

即使承诺恰好已经处于已解决状态,您也会始终看到“外部”和“内部”。Promise 是这样设计的,因此您可以确定执行顺序,因此不必修复涉及两个顺序的痛苦错误。当您使用await而不是显式.then's 时也是如此:await必须等到承诺决议之后的代码,并且呼叫堆栈回传到系统代码。

因此,当您从回呼切换到承诺时,您从可以在回圈中间同步呼叫回呼的代码转到必须等待执行回传的代码。Promise 之后updateLoop的代码只有一次回传或产生才能运行。由于该回圈在结束之前永远不会回传或产生,因此所有承诺都会延迟到回圈之后。

如果您需要updateLoop同步运行,那么您不能为此使用承诺。如果相反updateLoop应该是异步的(可能在运行下一步之前设定超时),那么可以使用承诺,但我需要更多关于您正在尝试做什么的详细信息来举例说明。

uj5u.com热心网友回复:

回答我自己的问题,因为我发现了一些提供预期功能的东西。我只是将示例中的 updateLoop() 呼叫更新为此。

  async updateLoop() {
    while (!this.windowShouldClose()) {
      await null;
      this.onUpdate.emit();
      this.update();
    }
  }

有人能更清楚地评论为什么现在会产生预期的效果吗?除非我添加 await 陈述句,否则将函式更改为 async 不会影响承诺决议的时间点。我的假设是包含一个 await 陈述句释放了最终允许承诺解决的事件回圈。如果您使用此函式编辑上面的 JSfiddle,则控制台中打印的数字将与打印陈述句“期望”的数字不一致 - 仅与计数器增加有关。执行顺序似乎是正确的。

标签:

0 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *