-
-
Notifications
You must be signed in to change notification settings - Fork 3.5k
[p5.js 2.0 Bug Report]: You need to await redraw() now #7770
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
The first method (Monitor the return values of lifecycles and only await them if they're async, so that draw is synchronous if all the lifecycle hooks are synchronous) has least friction to the end-user, but what's the maintenance cost exactly? And is this something that can be covered in test suite? |
It'd probably have to look something like this (pseudocode) to account for some hooks being synchronous and some maybe async, where we can jump to using promises halfway through: let donePromise
for (const hook of hooks) {
if (donePromise) {
donePromise = donePromise.then(() => hook())
} else {
const result = hook()
if (result instanceof Promise) {
donePromise = result
}
}
}
return donePromise Not terrible all things considered and unit tests will help! The other downside is that depending on what addons a user adds, they might have to start |
Although the implementation of draw/redraw (basically the draw loop including hooks) currently uses async await, I think an argument can be made for them to not be async at all. While it enables the draw loop itself to be async and thus you can use await promises in it, A downside to this is that setup and remove hooks can be async while draw hook cannot be so this can be an area of confusion. I'm also not 100% sure there are no good usage of async draw loop. So for example this part from Lines 378 to 385 in 55ed78e
We can potentially remove the |
The main async draw use case I can think of is something like ml5, where you want to do some processing on e.g. the webcam each frame to detect a pose. The processing is generally async. In 1.x examples, they don't block on the processing and just use whatever the last available pose is each frame, but this leads to the webcam being slightly ahead of the pose detection. Especially if one wants to record an export frame by frame, being able to ensure that each frame is complete when there's an async step would be beneficial. |
@quinton-ashley right, it's less the sync/async that matters but the return value. I think my implementation above handles that, it just checks if the return value is a promise or not in order to catch both async or synchronous promise-returning functions. |
Yep! So if no hooks return promises, then it never does any promise chaining that would need to be awaited. It still leaves something to be desired because a sketch author might not be aware of whether or not an add-on does something async, but I'm thinking maybe those are edge cases enough that those add-ons would explain their async usage in their own docs? |
@davepagurek Okay sorry I get how the pseudo-code works and what the goal is now. The point I was trying to get at though, is it seems like it'd really complicate the implementation of Documenting Or specifically saying "postsetup" and "presetup" hooks can be async but "init", "predraw", and "postdraw" hooks can not, seems preferable too. |
Making redraw always be async is also an option. If we go that route, we probably need to add code to detect when draw is running so that we don't start the next animation frame while a redraw from e.g. an event handler is being run. We would also need to decide to either special case an init hook to only ever be synchronous so that it can be run in the constructor, or adapt when we run global initialization hooks to come after an await. I think there are valid reasons to support async pre and post draw, e.g. #7770 (comment), so ideally we'd use an API that permits that. |
I'm thinking I prefer it to not need to be awaited as much as possible but I guess it might be confusing documentation wise?
This is likely possible if we want to do that, ie. we defer calling |
I think that sounds good to me. If that makes sense for everyone maybe a next step could be to prototype it just to double check that nothing weird happens with the animation frame deferral that we aren't expecting? |
Do go ahead, hopefully it won't messed up the next frame timing otherwise we'll have to debug the event loop itself. |
Most appropriate sub-area of p5.js?
p5.js version
2.0.0
Web browser and version
Firefox
Operating system
MacOS
Steps to reproduce this
Consider a sketch like this, where I want to export a higher res image on key press. Here's how I'd do this in 1.x:
However, in 2.0.0, this saves a blank image. Live: https://editor.p5js.org/davepagurek/sketches/GYscX3tl1
I think this is because our lifecycle hooks for pre/post draw are potentially async, and so they are internally awaited.
I can see two ways of resolving this, both valid:
await
them if they're async, so thatdraw
is synchronous if all the lifecycle hooks are synchronousredraw
must beawait
ed (and therefore functions callingredraw
must beasync
functions)I don't have a strong opinion on which approach is the right one here! The first one is probably more complex to maintain, but would be less of a breaking change from 1.x.
The text was updated successfully, but these errors were encountered: