在这里创建一个承诺,在那里解决它

Create a promise here, resolve it there

我以为我在所有情况下都有承诺的窍门,但我被困在这里:我希望动画在计时器上发生。我唯一能从图形包中得到的是帧数,所以我根据帧速率估计计算结束帧。

图形包是 p5.js,它公开了 frameCount,在每个绘制周期中仅以大约 60 fps 的速度运行。 (我在这个问题中将其命名为 currentFrame,希望能澄清)。据我所知,这是我在 p5.js

中进入动画帧状态的唯一可见性
doAnimation(duration) {
  // animation stuff
  this.endFrame = currentFrame + (60 * duration); // 60fps seems to be a good guess
}

draw() {
  if (currentFrame < this.endFrame) {
    // compute and draw the state of the animation
  } else if (currentFrame >= this.endFrame) {
    // animation done
  }
}

让我感到困惑的是如何给 doAnimation 调用者一个在其他方法中解决的承诺。我试过这个:

doAnimation(duration) {
  // animation stuff
  this.endFrame = currentFrame + (60 * duration);
  this.animationPromise = new Promise(resolve => {
    // I realize this is wrong, but it illustrates the problem. How do I run this test later?
    if (currentFrame >= this.endFrame) resolve();
  });
  return this.animationPromise;
}

draw() {
  if (currentFrame < this.endFrame) {
    // compute and draw the state of the animation
  } else if (currentFrame >= this.endFrame) {
    this.animationPromise.resolve();  // also wrong, I think
    // since resolve isn't an instance method of a promise
  }
}

有人能帮我澄清一下吗?

您需要存储解析器以调用:

而不是存储要解析的承诺
doAnimation(duration) {
  // animation stuff
  this.endFrame = currentFrame + (60 * duration);
  return new Promise(resolve => {
    this.onAnimationFinished = resolve;
  });
}

draw() {
  if (currentFrame < this.endFrame) {
    // compute and draw the state of the animation
  } else if (currentFrame >= this.endFrame) {
    this.onAnimationFinished();
  }
}

当然你需要确保一次只调用一次doAnimation(),并且draw()不会在doAnimation()之前被调用(或者更准确地说,onAnimationFinished 在设置 endFrame 时设置)。一旦到达结束帧,您可能还想将它们重置(undefined)。

一般来说,履行或拒绝承诺应该由在您传递的执行程序函数中启动的进程完成 new Promise。很少需要从外部履行或拒绝承诺(至少,调用者意识到这是承诺履行的方式)。

我会使用回调队列:

  1. 维护一个待定动画列表,其中包含它们结束的帧和回调。

  2. draw 在队列中查找它应该根据(新)currentFrame.

    调用的动画回调
  3. doAnimation代码排队回调

大致:

activeAnimations = [];

doAnimation(duration) {
    // animation stuff
    const endFrame = currentFrame + (60 * duration); // 60fps seems to be a good guess
    return new Promise(resolve => {
        this.activeAnimations.push({
            endFrame,
            done: resolve, // Or possibly: `done: () => resolve()`
                           // so this code is in control of whether
                           // there's a fulfillment value
        });
    });
}

draw() {
    for (let index = 0; index < this.activeAnimations; ++index) {
        const animation = this.activeAnimations[index];
        if (currentFrame > animation.endFrame) {
            // Animation done
            animation.done();
            this.activeAnimations.splice(index, 1);
            --index;
        } else {
            // Continue animation
        }
    }
}

done: () => resolve() 评论的原因是,一般来说,我尽量避免直接将 promise resolve/reject 函数暴露给 promise 执行程序之外的代码,根据它之外的代码通常不应该直接控制承诺发生的事情。 (我为 setTimeout 破例。:-))但这可能有点过分了。

作为替代方案,您还可以在 promise 构造函数回调中分配给 draw

function basicDraw() {
    // ...
}
var draw = basicDraw; // Default function

doAnimation(duration) {
    // animation stuff
    this.endFrame = currentFrame + (60 * duration);
    return new Promise((resolve) => {
        draw = () => {
            basicDraw();
            if (currentFrame < this.endFrame) {
                // compute and draw the state of the animation
            } else if (currentFrame >= this.endFrame) {
                draw = basicDraw; // back to normal
                resolve();
            }
        };
    });
}

doAnimation(1000).then(() => {
     /* whatever you want to do next */ 
});