'Create a promise here, resolve it there
I thought I had the hang of promises in all situations, but I'm stuck here: I'd like an animation to happen on a timer. The only thing I can get from the graphics package is the frame number, so I compute an end frame based on a frame rate estimate.
The graphics package is p5.js, which exposes frameCount, that just runs up at about 60 fps on each draw cycle. (I named it currentFrame in this question, hoping that would clarify). As far as I can tell, this is the only visibility I get into the animation frame state in 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
}
}
What I'm puzzled by is how to give the doAnimation caller a promise that resolves in the other method. I've tried this:
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
}
}
Can somebody get me un-mixed up about this?
Solution 1:[1]
In general, fulfilling or rejecting a promise should be done by the process that was started within the executor function you passed new Promise. It's very rare to need to fulfill or reject the promise from outside it (at least, in a way that the caller realizes it's a promise fulfillment).
I'd use a callback queue:
Maintain a list of pending animations which has the frame they end on and a callback.
Have
drawlook through the queue for animation callbacks it should call based on the (new)currentFrame.Have the
doAnimationcode queue a callback
Roughly:
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
}
}
}
The reason for the done: () => resolve() comment is that in general, I try to avoid directly exposing the promise resolve/reject functions to code outside the promise executor, on the basis that code outside it shouldn't typically be directly in control of what happens to the promise. (I make an exception for setTimeout. :-) ) But that may be overkill here.
Solution 2:[2]
As an alternative, you can also assign to draw within the promise constructor callback:
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 */
});
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 |
