Last active
August 24, 2018 12:10
-
-
Save l8on/7378d518a001a87adaf42a00fb7e50be to your computer and use it in GitHub Desktop.
Monitoring Jank Example
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
# Simple function that uses a heuristic to tell if a function | |
# is native code. Luckily this isn't security related code. | |
isNative = (fn) -> | |
return (/\{\s*\[native code\]\s*\}/).test('' + fn) | |
# An easy check to ensure we are on a modern browser | |
isModernBrowser = -> | |
return ( | |
window.addEventListener && | |
window.performance?.now && | |
isNative(window.requestAnimationFrame) | |
) | |
# Do nothing if this is not a modern browser | |
return unless isModernBrowser() | |
# Wait for the application to finish loading before setting up jank tracking. | |
window.addEventListener 'load', setupJankTracking | |
setupJankTracking = -> | |
# Browsers call back to `requestAnimationFrame` 60 times per second | |
# under smooth rendering conditions. | |
EXPECTED_RAF_DURATION = 1000.0 / 60.0 | |
# Only report jank if it is over this threshold | |
JANK_THRESHOLD = {{A_REASONABLE_THRESHOLD_FOR_YOUR_APP}} | |
# Create state in shared scope | |
jankState = | |
event: null | |
location: null | |
timerStart: null | |
onEvent = (event) -> | |
# Only track one event at a time. | |
return if jankState.event | |
jankState.event = event | |
jankState.location = window.location.href | |
jankState.timerStart = window.performance?.now() | |
window.requestAnimationFrame(onAnimationFrame) | |
onAnimationFrame = -> | |
# This can be negative as the next frame is not always 16ms away. | |
jankAmount = window.performance.now() - jankState.timerStart - EXPECTED_RAF_DURATION | |
# Some amount of jank is OK and mostly unnoticeable. | |
# Limit tracking to the out of the ordinary | |
if jankAmount > JANK_THRESHOLD | |
trackJank(jankAmount) | |
# Reset jankState so we capture the next event | |
jankState.event = null | |
jankState.location = null | |
jankState.timerStart = null | |
trackJank = (jankAmount) -> | |
# Ideally, your tracking infrastructure queues things up for the | |
# next event loop and doesn't block rendering. | |
window.monitoring.track { | |
jankAmount: jankAmount | |
querySelector: generateQuerySelector() | |
locationChanged: (window.location.href != jankState.location) | |
} | |
MAX_DEPTH = {{A_REASONABLE_DEPTH_FOR_YOUR_APP}} | |
generateQuerySelector = -> | |
# Watch out for synthetic events that don't have a target! | |
return '' unless jankState.event.target | |
# You can imagine what traverseUpDOMTree would look like ;) | |
querySelector = traverseUpDOMTree(MAX_DEPTH) | |
return querySelector | |
# We use event capturing here (the third param) to set up jank | |
# monitoring before standard event handlers do their work. | |
# Also, we attach to the window so the event listeners are not | |
# destroyed on navigation. | |
window.addEventListener('click', onEvent, true) | |
window.addEventListener('keydown', onEvent, true) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment