mirror of https://github.com/vuejs/core.git
fix(scheduler): job ordering when the post queue is flushing (#12090)
This commit is contained in:
parent
3a55c3e421
commit
577edca8e7
|
@ -441,6 +441,29 @@ describe('scheduler', () => {
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(calls).toEqual(['job1', 'job2', 'cb1', 'cb2'])
|
expect(calls).toEqual(['job1', 'job2', 'cb1', 'cb2'])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('jobs added during post flush are ordered correctly', async () => {
|
||||||
|
const calls: string[] = []
|
||||||
|
|
||||||
|
const job1: SchedulerJob = () => {
|
||||||
|
calls.push('job1')
|
||||||
|
}
|
||||||
|
job1.id = 1
|
||||||
|
|
||||||
|
const job2: SchedulerJob = () => {
|
||||||
|
calls.push('job2')
|
||||||
|
}
|
||||||
|
job2.id = 2
|
||||||
|
|
||||||
|
queuePostFlushCb(() => {
|
||||||
|
queueJob(job2)
|
||||||
|
queueJob(job1)
|
||||||
|
})
|
||||||
|
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
expect(calls).toEqual(['job1', 'job2'])
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('sort job based on id', async () => {
|
test('sort job based on id', async () => {
|
||||||
|
@ -758,6 +781,37 @@ describe('scheduler', () => {
|
||||||
expect(spy).toHaveBeenCalledTimes(1)
|
expect(spy).toHaveBeenCalledTimes(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('flushPreFlushCbs inside a post job', async () => {
|
||||||
|
const calls: string[] = []
|
||||||
|
const callsAfterFlush: string[] = []
|
||||||
|
|
||||||
|
const job1: SchedulerJob = () => {
|
||||||
|
calls.push('job1')
|
||||||
|
}
|
||||||
|
job1.id = 1
|
||||||
|
job1.flags! |= SchedulerJobFlags.PRE
|
||||||
|
|
||||||
|
const job2: SchedulerJob = () => {
|
||||||
|
calls.push('job2')
|
||||||
|
}
|
||||||
|
job2.id = 2
|
||||||
|
job2.flags! |= SchedulerJobFlags.PRE
|
||||||
|
|
||||||
|
queuePostFlushCb(() => {
|
||||||
|
queueJob(job2)
|
||||||
|
queueJob(job1)
|
||||||
|
|
||||||
|
// e.g. nested app.mount() call
|
||||||
|
flushPreFlushCbs()
|
||||||
|
callsAfterFlush.push(...calls)
|
||||||
|
})
|
||||||
|
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
expect(callsAfterFlush).toEqual(['job1', 'job2'])
|
||||||
|
expect(calls).toEqual(['job1', 'job2'])
|
||||||
|
})
|
||||||
|
|
||||||
it('nextTick should return promise', async () => {
|
it('nextTick should return promise', async () => {
|
||||||
const fn = vi.fn(() => {
|
const fn = vi.fn(() => {
|
||||||
return 1
|
return 1
|
||||||
|
|
|
@ -40,11 +40,8 @@ export interface SchedulerJob extends Function {
|
||||||
|
|
||||||
export type SchedulerJobs = SchedulerJob | SchedulerJob[]
|
export type SchedulerJobs = SchedulerJob | SchedulerJob[]
|
||||||
|
|
||||||
let isFlushing = false
|
|
||||||
let isFlushPending = false
|
|
||||||
|
|
||||||
const queue: SchedulerJob[] = []
|
const queue: SchedulerJob[] = []
|
||||||
let flushIndex = 0
|
let flushIndex = -1
|
||||||
|
|
||||||
const pendingPostFlushCbs: SchedulerJob[] = []
|
const pendingPostFlushCbs: SchedulerJob[] = []
|
||||||
let activePostFlushCbs: SchedulerJob[] | null = null
|
let activePostFlushCbs: SchedulerJob[] | null = null
|
||||||
|
@ -74,7 +71,7 @@ export function nextTick<T = void, R = void>(
|
||||||
// watcher should be inserted immediately before the update job. This allows
|
// watcher should be inserted immediately before the update job. This allows
|
||||||
// watchers to be skipped if the component is unmounted by the parent update.
|
// watchers to be skipped if the component is unmounted by the parent update.
|
||||||
function findInsertionIndex(id: number) {
|
function findInsertionIndex(id: number) {
|
||||||
let start = isFlushing ? flushIndex + 1 : 0
|
let start = flushIndex + 1
|
||||||
let end = queue.length
|
let end = queue.length
|
||||||
|
|
||||||
while (start < end) {
|
while (start < end) {
|
||||||
|
@ -115,8 +112,7 @@ export function queueJob(job: SchedulerJob): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
function queueFlush() {
|
function queueFlush() {
|
||||||
if (!isFlushing && !isFlushPending) {
|
if (!currentFlushPromise) {
|
||||||
isFlushPending = true
|
|
||||||
currentFlushPromise = resolvedPromise.then(flushJobs)
|
currentFlushPromise = resolvedPromise.then(flushJobs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,8 +137,8 @@ export function queuePostFlushCb(cb: SchedulerJobs): void {
|
||||||
export function flushPreFlushCbs(
|
export function flushPreFlushCbs(
|
||||||
instance?: ComponentInternalInstance,
|
instance?: ComponentInternalInstance,
|
||||||
seen?: CountMap,
|
seen?: CountMap,
|
||||||
// if currently flushing, skip the current job itself
|
// skip the current job
|
||||||
i: number = isFlushing ? flushIndex + 1 : 0,
|
i: number = flushIndex + 1,
|
||||||
): void {
|
): void {
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
seen = seen || new Map()
|
seen = seen || new Map()
|
||||||
|
@ -211,8 +207,6 @@ const getId = (job: SchedulerJob): number =>
|
||||||
job.id == null ? (job.flags! & SchedulerJobFlags.PRE ? -1 : Infinity) : job.id
|
job.id == null ? (job.flags! & SchedulerJobFlags.PRE ? -1 : Infinity) : job.id
|
||||||
|
|
||||||
function flushJobs(seen?: CountMap) {
|
function flushJobs(seen?: CountMap) {
|
||||||
isFlushPending = false
|
|
||||||
isFlushing = true
|
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
seen = seen || new Map()
|
seen = seen || new Map()
|
||||||
}
|
}
|
||||||
|
@ -255,15 +249,13 @@ function flushJobs(seen?: CountMap) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
flushIndex = 0
|
flushIndex = -1
|
||||||
queue.length = 0
|
queue.length = 0
|
||||||
|
|
||||||
flushPostFlushCbs(seen)
|
flushPostFlushCbs(seen)
|
||||||
|
|
||||||
isFlushing = false
|
|
||||||
currentFlushPromise = null
|
currentFlushPromise = null
|
||||||
// some postFlushCb queued jobs!
|
// If new jobs have been added to either queue, keep flushing
|
||||||
// keep flushing until it drains.
|
|
||||||
if (queue.length || pendingPostFlushCbs.length) {
|
if (queue.length || pendingPostFlushCbs.length) {
|
||||||
flushJobs(seen)
|
flushJobs(seen)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue