Last active
May 28, 2024 17:20
-
-
Save noamr/316bd48157ab35e4f632a8c2583281b7 to your computer and use it in GitHub Desktop.
WhyNP: Analyze LoAFs & event timing entries to find cause of bad INP
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function() { | |
let pending_loaf_entries = []; | |
let pending_event_entries = []; | |
let timeout_handle = null; | |
const combined_map = new Map(); | |
function print() { | |
const entries = [...combined_map.entries()].sort((a, b) => b.duration - a.duration); | |
console.log(entries.map(([loaf, event]) => { | |
let longest_script = null; | |
let total_thrashing_duration = 0; | |
for (const script of loaf.scripts) { | |
total_thrashing_duration += script.forcedStyleAndLayoutDuration; | |
if (script.duration > (longest_script?.duration ?? 0)) | |
longest_script = script; | |
} | |
return { | |
loaf_blocking_duration: loaf.blockingDuration, | |
event_duration: event.duration, | |
longest_script, | |
total_thrashing_duration, | |
render_time: (loaf.startTime + loaf.duration - loaf.renderStart), | |
loaf, event | |
}; | |
})); | |
} | |
function process() { | |
for (const event_entry of pending_event_entries) { | |
for (const loaf_entry of pending_loaf_entries) { | |
if (!loaf_entry.blockingDuration) | |
continue; | |
/* This LoAF ends before this event starts.*/ | |
if ((loaf_entry.startTime + loaf_entry.duration) < event_entry.startTime) | |
continue; | |
/* This LoAF starts after this event ends. */ | |
if (loaf_entry.startTime > (event_entry.startTime + event_entry.duration)) | |
break; | |
if (event_entry.duration > (combined_map.get(loaf_entry)?.duration ?? 0)) | |
combined_map.set(loaf_entry, event_entry); | |
} | |
} | |
pending_loaf_entries = []; | |
pending_event_entries = []; | |
print(); | |
} | |
function schedule_process() { | |
// Debounce, to make sure all the relevant entries are in. | |
if (timeout_handle) | |
clearTimeout(timeout_handle); | |
timeout_handle = setTimeout(() => { | |
timeout_handle = null; | |
process(); | |
}, 1000); | |
} | |
new PerformanceObserver(entries => { | |
pending_loaf_entries = [...pending_loaf_entries, ...entries.getEntries()]; | |
}).observe({type: "long-animation-frame", buffered: true}); | |
new PerformanceObserver(entries => { | |
const unique_entries = Object.values(Object.fromEntries(entries.getEntries().map(entry => [JSON.stringify(entry), entry]))); | |
pending_event_entries = [...pending_event_entries, ...unique_entries]; | |
schedule_process(); | |
}).observe({type: "event", buffered: true, durationThreshold: 150}); | |
})(); |
@noamr Sorry for the delayed ping :/
This gist is discontinued, it's integrated into https://github.com/GoogleChrome/web-vitals
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Could you please explain the code if possible ? I had injected the above function into my console where I was running my Nextjs application built over Typescript as described here in this issue: GoogleChrome/web-vitals#489
Following is the output received form the above script:
My input was clicking on the
Add Item (4s)
button and immediately then clicking on theShow
button to display the animation frame. The code and the deployed website is linked in the above issue for reference.UI:
Console:
Please note that this was simulated on the Iphone 14 Pro Max dimensions alonside 4x CPU Throttling.
Could you please tell what new insights the above script provides apart from the regular web-vitals-attribution output from the
onINP()
function ? The source function name and URL seems to differ from the expected output as I have described in the issue.Thanks a lot in advance!