Promises, async functions, and requestAnimationFrame, together.

Matthew Robb matthewwrobb at gmail.com
Tue Apr 26 14:45:28 UTC 2016


This is definitely interesting stuff. Have you considered rewriting this so
that it only uses generators? If you did then you could test natively in
Chrome and see if you get the same results.


- Matthew Robb

On Sat, Apr 23, 2016 at 7:01 PM, /#!/JoePea <joe at trusktr.io> wrote:

> Just to show a little more detail, here's a screenshot that shows that
> the logic of the while-loop version of my animation loop fires inside
> each animation frame. I've zoomed out and we can see there's nothing
> fired between the frames:
>
>
> https://cloud.githubusercontent.com/assets/297678/14764323/c28e83cc-0968-11e6-8771-8e726158aa52.png
>
> On Sat, Apr 23, 2016 at 3:18 PM, /#!/JoePea <joe at trusktr.io> wrote:
> > Alright, I did an experiment, and I'm really surprised at the results!
> > Apparently, the logic (what would be drawSomething() in my previous
> > example) is fired within the frame!!
> >
> > So, let me show you my original method for starting an animation loop.
> > I'm working on a 3D project at http://infamous.io. The Scene class
> > (https://github.com/infamous/infamous/blob/master/src/motor/Scene.js)
> > has a method for starting an animation loop the standard way:
> >
> > ```js
> >     async _startAnimationLoopWhenMounted() {
> >         this._animationLoopStarted = true
> >
> >         if (!this._mounted) await this.mountPromise
> >
> >         // So now we can render after the scene is mounted.
> >         const loop = timestamp => {
> >             this._inFrame = true
> >
> >             this._runRenderTasks(timestamp)
> >             this._renderNodes(timestamp)
> >
> >             // If any tasks are left to run, continue the animation loop.
> >             if (this._allRenderTasks.length)
> >                 this._rAF = requestAnimationFrame(loop)
> >             else {
> >                 this._rAF = null
> >                 this._animationLoopStarted = false
> >             }
> >
> >             this._inFrame = false
> >         }
> >
> >         this._rAF = requestAnimationFrame(loop)
> >     }
> > ```
> >
> > Here's what the Chrome timeline shows for the logic that is fired
> > inside the loop:
> >
> https://cloud.githubusercontent.com/assets/297678/14764236/8eb72d4a-0965-11e6-9bb9-5db02cc23520.png
> >
> > Now, I went ahead and modified my Scene class so the method now looks
> like this:
> >
> > ```js
> > function animationFrame() {
> >     let resolve = null
> >     const promise = new Promise(r => resolve = r)
> >     window.requestAnimationFrame(resolve)
> >     return promise
> > }
> >
> > // ...
> >
> >     async _startAnimationLoopWhenMounted() {
> >         this._animationLoopStarted = true
> >
> >         if (!this._mounted) await this.mountPromise
> >
> >         this._rAF = true
> >         let timestamp = null
> >         while (this._rAF) {
> >             timestamp = await animationFrame()
> >             this._inFrame = true
> >
> >             this._runRenderTasks(timestamp)
> >             this._renderNodes(timestamp)
> >
> >             // If any tasks are left to run, continue the animation loop.
> >             if (!this._allRenderTasks.length) {
> >                 this._rAF = null
> >                 this._animationLoopStarted = false
> >             }
> >
> >             this._inFrame = false
> >         }
> >     }
> > ```
> >
> > And the timeline results are surprising! As you can see in the
> > following screenshot, all of the logic happens within the frame
> > (though you can see there's extra overhead from what I assume are the
> > extra function calls due to the fact that I'm using Facebook
> > Regenerator for the async functions):
> >
> https://cloud.githubusercontent.com/assets/297678/14764237/8eb71ce2-0965-11e6-942a-3c556c48b9a0.png
> >
> > Near the bottom right of the screen shot, you can see the tooltip as
> > I'm hovering on the call to `animationFrame` which returns the promise
> > that I am awaiting in the loop.
> >
> > Although this behavior seems to be exactly what I was hoping for, it
> > seems like there is something wrong. Could there be a bug in
> > regenerator that is failing to defer my loop code to a following tick?
> > Or is my code deferred to a following tick that somehow the animation
> > frame knows to execute within the same tick? Maybe there's something
> > I'm missing about the Promise API that allows for .then() of a promise
> > (which I assume is what Regenerator is using) to be executed in the
> > same tick? What's going on here?
> >
> > I was expecting to see the code of my loop execute outside of the
> > "Animation Frame Fired" section.
> >
> > On Sat, Apr 23, 2016 at 6:28 AM, Boris Zbarsky <bzbarsky at mit.edu> wrote:
> >> On 4/23/16 4:09 AM, Salvador de la Puente González wrote:
> >>>
> >>> AFAIK, that should execute `drawSomething()` once per frame. Given a
> >>> frame is each time the animatinFrame() promise resolves.
> >>
> >>
> >> What's not obvious to me is whether it will execute it before the actual
> >> paint for the frame (i.e. before or right after the loop that's
> notifying
> >> the animation frame callbacks completes) or whether it will do
> >> drawSomething() after the paint...
> >>
> >> -Boris
> >>
> >> _______________________________________________
> >> es-discuss mailing list
> >> es-discuss at mozilla.org
> >> https://mail.mozilla.org/listinfo/es-discuss
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20160426/b105be6e/attachment.html>


More information about the es-discuss mailing list