Skip to content

Instantly share code, notes, and snippets.

@l8on
Last active August 24, 2018 12:10
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save l8on/7378d518a001a87adaf42a00fb7e50be to your computer and use it in GitHub Desktop.
Save l8on/7378d518a001a87adaf42a00fb7e50be to your computer and use it in GitHub Desktop.
Monitoring Jank Example
# 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