Skip to content

Commit

Permalink
fix(scheduler): ensure updates are always inserted in ascending id or…
Browse files Browse the repository at this point in the history
…der (#3184)

fix #2768, fix #2829
  • Loading branch information
HcySunYang authored Feb 25, 2021
1 parent 75964ab commit 45fae9d
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 1 deletion.
32 changes: 32 additions & 0 deletions packages/runtime-core/__tests__/scheduler.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,38 @@ describe('scheduler', () => {
expect(calls).toEqual(['job1', 'job2'])
})

it("should insert jobs in ascending order of job's id when flushing", async () => {
const calls: string[] = []
const job1 = () => {
calls.push('job1')

queueJob(job2)
queueJob(job3)
queueJob(job4)
}

const job2 = () => {
calls.push('job2')
}
job2.id = 10

const job3 = () => {
calls.push('job3')
}
job3.id = 1

// job4 gets the Infinity as it's id
const job4 = () => {
calls.push('job4')
}

queueJob(job1)

expect(calls).toEqual([])
await nextTick()
expect(calls).toEqual(['job1', 'job3', 'job2', 'job4'])
})

it('should dedupe queued jobs', async () => {
const calls: string[] = []
const job1 = () => {
Expand Down
26 changes: 25 additions & 1 deletion packages/runtime-core/src/scheduler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,25 @@ export function nextTick(
return fn ? p.then(this ? fn.bind(this) : fn) : p
}

// #2768
// Use binary-search to find a suitable position in the queue,
// so that the queue maintains the increasing order of job's id,
// which can prevent the job from being skipped and also can avoid repeated patching.
function findInsertionIndex(job: SchedulerJob) {
// the start index should be `flushIndex + 1`
let start = flushIndex + 1
let end = queue.length
const jobId = getId(job)

while (start < end) {
const middle = (start + end) >>> 1
const middleJobId = getId(queue[middle])
middleJobId < jobId ? (start = middle + 1) : (end = middle)
}

return start
}

export function queueJob(job: SchedulerJob) {
// the dedupe search uses the startIndex argument of Array.includes()
// by default the search index includes the current job that is being run
Expand All @@ -72,7 +91,12 @@ export function queueJob(job: SchedulerJob) {
)) &&
job !== currentPreFlushParentJob
) {
queue.push(job)
const pos = findInsertionIndex(job)
if (pos > -1) {
queue.splice(pos, 0, job)
} else {
queue.push(job)
}
queueFlush()
}
}
Expand Down

0 comments on commit 45fae9d

Please sign in to comment.