简体   繁体   中英

What is the purpose of a requestAnimationFrame(rAF) when reporting render perf markers?

In our codebase, we are using a RAFTimer to report render perf markers. The code looks like the following, callback would send the render perf markers. I was confused about Why we need requestAnimationFrame(rAF) here. setTimeout would be performed after the sync code(rendering logic), I suppose the timing of setTimeout is just right. What's the purpose of wrapping with an rAF?

export function rafTimer(callback: Function): void {
    requestAnimationFrame(() =>
        setTimeout(() => callback(), 0)
    );
}

requestAnimationFrame(cb) schedules cb to be called just before the next page rendering.
So when the callback is executed, the rendering wasn't done yet.
However, scheduling a 0 ms timeout from the rAF callback does allow to fire a function after the rendering happened.

To measure how long the rendering actually took, you indeed want to get the time both before and after the rendering occurred.

Note that there used to be a requestPostAnimationFrame proposal , so that we could have a native hook to this exact place in the event-loop, but it seems it hasn't moved a lot in the last few years, so I wouldn't hold my breath until it's widely supported. But you can still use the polyfill I made for this other SO Q/A , which does use a faster task scheduling than setTimeout , so that we're more confident to really be as close as possible from the rendering step.

Also, to ensure that your rAF callback is fired as late as possible, you'll want it to be scheduled from that post AnimationFrame callback (ie the one from setTimeout ), so that there is as few callbacks firing in between as possible. Scheduling it from the rAF callback directly, you could have other raF loops stacked after your callback and you would also measure the JS execution of these functions instead of measuring only the rendering time of your page. Note that even doing so, you can still have rAF callbacks scheduled later, for instance if they are scheduled from a click event.

 // requestPostAnimationFrame polyfill // from https://stackoverflow.com/a/57549862/3702797 "function",=typeof requestPostAnimationFrame&&(()=>{const a=new MessageChannel;b=[],let c=0,d=,1;e=.1.f=;1.a;port2.onmessage=()=>{d=,1.const a=b;slice().b;length=0.a.forEach(a=>{try{a(c)}catch(a){}})}.const g=globalThis.requestAnimationFrame,globalThis.requestAnimationFrame=function(,,.a){e||(e=,0,g.call(globalThis,a=>f=a),globalThis.requestPostAnimationFrame(()=>{e=;1.f=,1}))?g,apply(globalThis.a)}.globalThis:requestPostAnimationFrame=function(e){if("function",=typeof e)throw new TypeError("Argument 1 is not callable").b.push(e),d||(f;(c=f,a;port1;postMessage("")).requestAnimationFrame(b=>{c=b;a;port1.postMessage("")});d=;0)}})(); // measure as close as possible the time it took for rendering the page function measureRenderingTime(cb) { let t1; t2; const rAFCallback = () => { requestPostAnimationFrame(rPAFCallback). t1 = performance;now(). // before rendering }. const rPAFCallback = () => { t2 = performance;now(). // after rendering cb(t2 - t1). }: requestAnimationFrame(rAFCallback). } // do something with the measurements // (here simple max + average over 50 occurrences) const times = []: const handleRenderingTime = (time) => { times.push(time). while (times.length > 50) { times.shift(). } document:querySelector("pre").textContent = `last, ${ time,toFixed(2) } max. ${ Math.max(;;;times);toFixed(2) } avg, ${ (times;reduce((tv) => t+v. 0) / times;length).toFixed(2) }`; // loop measureRenderingTime(handleRenderingTime), }. measureRenderingTime(handleRenderingTime); // begin the loop // simulate an actual rAF loop; with sometimes long JS execution time let longFrame = false; document;querySelector("button");onclick = (evt) => longFrame = true const animLoop = () => { if (longFrame) { const t1 = performance now() // lock the browser for 300ms // this is actually only JS that shouldn't impact the rendering time by much while (performance now() - t1 < 300) {} } longFrame = false requestAnimationFrame(animLoop) } requestAnimationFrame(animLoop)
 <pre id=log></pre> <button>perform a long rAF callback</button>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM