refactor: cache calls to performance.now

This commit is contained in:
Evan You 2019-01-23 18:52:05 -05:00
parent 360a10fff2
commit 16270edda5
2 changed files with 22 additions and 14 deletions

View File

@ -1,5 +1,3 @@
import { isChrome } from '../ua'
interface Invoker extends Function { interface Invoker extends Function {
value: EventValue value: EventValue
lastUpdated?: number lastUpdated?: number
@ -9,6 +7,23 @@ type EventValue = (Function | Function[]) & {
invoker?: Invoker | null invoker?: Invoker | null
} }
// async edge case fix requires storing an event listener's attach timestamp
// to avoid the overhead of repeatedly calling performance.now(), we cache
// and use the same timestamp for all event listners attached in the same tick.
let cachedNow: number = 0
const p = Promise.resolve()
function getNow() {
if (cachedNow) {
return cachedNow
} else {
p.then(() => {
cachedNow = 0
})
return (cachedNow = performance.now())
}
}
export function patchEvent( export function patchEvent(
el: Element, el: Element,
name: string, name: string,
@ -21,9 +36,7 @@ export function patchEvent(
;(prevValue as EventValue).invoker = null ;(prevValue as EventValue).invoker = null
invoker.value = nextValue invoker.value = nextValue
nextValue.invoker = invoker nextValue.invoker = invoker
if (isChrome) { invoker.lastUpdated = getNow()
invoker.lastUpdated = performance.now()
}
} else { } else {
el.addEventListener(name, createInvoker(nextValue)) el.addEventListener(name, createInvoker(nextValue))
} }
@ -38,20 +51,18 @@ function createInvoker(value: any) {
}) as any }) as any
invoker.value = value invoker.value = value
value.invoker = invoker value.invoker = invoker
if (isChrome) { invoker.lastUpdated = getNow()
invoker.lastUpdated = performance.now()
}
return invoker return invoker
} }
function invokeEvents(e: Event, value: EventValue, lastUpdated: number) { function invokeEvents(e: Event, value: EventValue, lastUpdated: number) {
// async edge case #6566: inner click event triggers patch, event handler // async edge case #6566: inner click event triggers patch, event handler
// attached to outer element during patch, and triggered again. This only // attached to outer element during patch, and triggered again. This
// happens in Chrome as it fires microtask ticks between event propagation. // happens because browsers fire microtask ticks between event propagation.
// the solution is simple: we save the timestamp when a handler is attached, // the solution is simple: we save the timestamp when a handler is attached,
// and the handler would only fire if the event passed to it was fired // and the handler would only fire if the event passed to it was fired
// AFTER it was attached. // AFTER it was attached.
if (isChrome && e.timeStamp < lastUpdated) { if (e.timeStamp < lastUpdated) {
return return
} }

View File

@ -1,3 +0,0 @@
export const UA = window.navigator.userAgent.toLowerCase()
export const isEdge = UA.indexOf('edge/') > 0
export const isChrome = /chrome\/\d+/.test(UA) && !isEdge