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()
|
||||
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 isFlushPending = false
|
||||
let flushIndex = 0
|
||||
let pendingPostFlushCbs: Function[] | null = null
|
||||
let pendingPostFlushIndex = 0
|
||||
|
||||
const RECURSION_LIMIT = 100
|
||||
type CountMap = Map<Job | Function, number>
|
||||
@ -21,7 +24,7 @@ export function nextTick(fn?: () => void): Promise<void> {
|
||||
}
|
||||
|
||||
export function queueJob(job: Job) {
|
||||
if (!queue.includes(job)) {
|
||||
if (!queue.includes(job, flushIndex)) {
|
||||
queue.push(job)
|
||||
queueFlush()
|
||||
}
|
||||
@ -36,8 +39,16 @@ export function invalidateJob(job: Job) {
|
||||
|
||||
export function queuePostFlushCb(cb: Function | Function[]) {
|
||||
if (!isArray(cb)) {
|
||||
postFlushCbs.push(cb)
|
||||
if (
|
||||
!pendingPostFlushCbs ||
|
||||
!pendingPostFlushCbs.includes(cb, pendingPostFlushIndex)
|
||||
) {
|
||||
postFlushCbs.push(cb)
|
||||
}
|
||||
} 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)
|
||||
}
|
||||
queueFlush()
|
||||
@ -52,17 +63,23 @@ function queueFlush() {
|
||||
|
||||
export function flushPostFlushCbs(seen?: CountMap) {
|
||||
if (postFlushCbs.length) {
|
||||
const cbs = [...new Set(postFlushCbs)]
|
||||
pendingPostFlushCbs = [...new Set(postFlushCbs)]
|
||||
postFlushCbs.length = 0
|
||||
if (__DEV__) {
|
||||
seen = seen || new Map()
|
||||
}
|
||||
for (let i = 0; i < cbs.length; i++) {
|
||||
for (
|
||||
pendingPostFlushIndex = 0;
|
||||
pendingPostFlushIndex < pendingPostFlushCbs.length;
|
||||
pendingPostFlushIndex++
|
||||
) {
|
||||
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) {
|
||||
isFlushPending = false
|
||||
isFlushing = true
|
||||
let job
|
||||
if (__DEV__) {
|
||||
seen = seen || new Map()
|
||||
}
|
||||
@ -87,15 +103,18 @@ function flushJobs(seen?: CountMap) {
|
||||
// during execution of another flushed job.
|
||||
queue.sort((a, b) => getId(a!) - getId(b!))
|
||||
|
||||
while ((job = queue.shift()) !== undefined) {
|
||||
if (job === null) {
|
||||
continue
|
||||
for (flushIndex = 0; flushIndex < queue.length; flushIndex++) {
|
||||
const job = queue[flushIndex]
|
||||
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)
|
||||
isFlushing = false
|
||||
// some postFlushCb queued jobs!
|
||||
|
Loading…
Reference in New Issue
Block a user