perf(runtime-core): avoid duplicate postFlushCb invocation
Also improve flush performance by using for loop instead of shift() fix #1595
This commit is contained in:
parent
aab99abd28
commit
165068dbc2
@ -278,4 +278,20 @@ describe('scheduler', () => {
|
|||||||
await nextTick()
|
await nextTick()
|
||||||
expect(calls).toEqual(['job3', 'job2', 'job1'])
|
expect(calls).toEqual(['job3', 'job2', 'job1'])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// #1595
|
||||||
|
test('avoid duplicate postFlushCb invocation', async () => {
|
||||||
|
const calls: string[] = []
|
||||||
|
const cb1 = () => {
|
||||||
|
calls.push('cb1')
|
||||||
|
queuePostFlushCb(cb2)
|
||||||
|
}
|
||||||
|
const cb2 = () => {
|
||||||
|
calls.push('cb2')
|
||||||
|
}
|
||||||
|
queuePostFlushCb(cb1)
|
||||||
|
queuePostFlushCb(cb2)
|
||||||
|
await nextTick()
|
||||||
|
expect(calls).toEqual(['cb1', 'cb2'])
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -12,6 +12,9 @@ const p = Promise.resolve()
|
|||||||
|
|
||||||
let isFlushing = false
|
let isFlushing = false
|
||||||
let isFlushPending = false
|
let isFlushPending = false
|
||||||
|
let flushIndex = 0
|
||||||
|
let pendingPostFlushCbs: Function[] | null = null
|
||||||
|
let pendingPostFlushIndex = 0
|
||||||
|
|
||||||
const RECURSION_LIMIT = 100
|
const RECURSION_LIMIT = 100
|
||||||
type CountMap = Map<Job | Function, number>
|
type CountMap = Map<Job | Function, number>
|
||||||
@ -21,7 +24,7 @@ export function nextTick(fn?: () => void): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function queueJob(job: Job) {
|
export function queueJob(job: Job) {
|
||||||
if (!queue.includes(job)) {
|
if (!queue.includes(job, flushIndex)) {
|
||||||
queue.push(job)
|
queue.push(job)
|
||||||
queueFlush()
|
queueFlush()
|
||||||
}
|
}
|
||||||
@ -36,8 +39,16 @@ export function invalidateJob(job: Job) {
|
|||||||
|
|
||||||
export function queuePostFlushCb(cb: Function | Function[]) {
|
export function queuePostFlushCb(cb: Function | Function[]) {
|
||||||
if (!isArray(cb)) {
|
if (!isArray(cb)) {
|
||||||
postFlushCbs.push(cb)
|
if (
|
||||||
|
!pendingPostFlushCbs ||
|
||||||
|
!pendingPostFlushCbs.includes(cb, pendingPostFlushIndex)
|
||||||
|
) {
|
||||||
|
postFlushCbs.push(cb)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// if cb is an array, it is a component lifecycle hook which can only be
|
||||||
|
// triggered by a job, which is already deduped in the main queue, so
|
||||||
|
// we can skip dupicate check here to improve perf
|
||||||
postFlushCbs.push(...cb)
|
postFlushCbs.push(...cb)
|
||||||
}
|
}
|
||||||
queueFlush()
|
queueFlush()
|
||||||
@ -52,17 +63,23 @@ function queueFlush() {
|
|||||||
|
|
||||||
export function flushPostFlushCbs(seen?: CountMap) {
|
export function flushPostFlushCbs(seen?: CountMap) {
|
||||||
if (postFlushCbs.length) {
|
if (postFlushCbs.length) {
|
||||||
const cbs = [...new Set(postFlushCbs)]
|
pendingPostFlushCbs = [...new Set(postFlushCbs)]
|
||||||
postFlushCbs.length = 0
|
postFlushCbs.length = 0
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
seen = seen || new Map()
|
seen = seen || new Map()
|
||||||
}
|
}
|
||||||
for (let i = 0; i < cbs.length; i++) {
|
for (
|
||||||
|
pendingPostFlushIndex = 0;
|
||||||
|
pendingPostFlushIndex < pendingPostFlushCbs.length;
|
||||||
|
pendingPostFlushIndex++
|
||||||
|
) {
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
checkRecursiveUpdates(seen!, cbs[i])
|
checkRecursiveUpdates(seen!, pendingPostFlushCbs[pendingPostFlushIndex])
|
||||||
}
|
}
|
||||||
cbs[i]()
|
pendingPostFlushCbs[pendingPostFlushIndex]()
|
||||||
}
|
}
|
||||||
|
pendingPostFlushCbs = null
|
||||||
|
pendingPostFlushIndex = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +88,6 @@ const getId = (job: Job) => (job.id == null ? Infinity : job.id)
|
|||||||
function flushJobs(seen?: CountMap) {
|
function flushJobs(seen?: CountMap) {
|
||||||
isFlushPending = false
|
isFlushPending = false
|
||||||
isFlushing = true
|
isFlushing = true
|
||||||
let job
|
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
seen = seen || new Map()
|
seen = seen || new Map()
|
||||||
}
|
}
|
||||||
@ -87,15 +103,18 @@ function flushJobs(seen?: CountMap) {
|
|||||||
// during execution of another flushed job.
|
// during execution of another flushed job.
|
||||||
queue.sort((a, b) => getId(a!) - getId(b!))
|
queue.sort((a, b) => getId(a!) - getId(b!))
|
||||||
|
|
||||||
while ((job = queue.shift()) !== undefined) {
|
for (flushIndex = 0; flushIndex < queue.length; flushIndex++) {
|
||||||
if (job === null) {
|
const job = queue[flushIndex]
|
||||||
continue
|
if (job) {
|
||||||
|
if (__DEV__) {
|
||||||
|
checkRecursiveUpdates(seen!, job)
|
||||||
|
}
|
||||||
|
callWithErrorHandling(job, null, ErrorCodes.SCHEDULER)
|
||||||
}
|
}
|
||||||
if (__DEV__) {
|
|
||||||
checkRecursiveUpdates(seen!, job)
|
|
||||||
}
|
|
||||||
callWithErrorHandling(job, null, ErrorCodes.SCHEDULER)
|
|
||||||
}
|
}
|
||||||
|
flushIndex = 0
|
||||||
|
queue.length = 0
|
||||||
|
|
||||||
flushPostFlushCbs(seen)
|
flushPostFlushCbs(seen)
|
||||||
isFlushing = false
|
isFlushing = false
|
||||||
// some postFlushCb queued jobs!
|
// some postFlushCb queued jobs!
|
||||||
|
Loading…
x
Reference in New Issue
Block a user