Promises, async functions, and requestAnimationFrame, together.

/#!/JoePea joe at trusktr.io
Sat Apr 23 23:01:29 UTC 2016


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


More information about the es-discuss mailing list