Skip to content

Commit 45fae9d

Browse files
authored
fix(scheduler): ensure updates are always inserted in ascending id order (#3184)
fix #2768, fix #2829
1 parent 75964ab commit 45fae9d

File tree

2 files changed

+57
-1
lines changed

2 files changed

+57
-1
lines changed

packages/runtime-core/__tests__/scheduler.spec.ts

+32
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,38 @@ describe('scheduler', () => {
4444
expect(calls).toEqual(['job1', 'job2'])
4545
})
4646

47+
it("should insert jobs in ascending order of job's id when flushing", async () => {
48+
const calls: string[] = []
49+
const job1 = () => {
50+
calls.push('job1')
51+
52+
queueJob(job2)
53+
queueJob(job3)
54+
queueJob(job4)
55+
}
56+
57+
const job2 = () => {
58+
calls.push('job2')
59+
}
60+
job2.id = 10
61+
62+
const job3 = () => {
63+
calls.push('job3')
64+
}
65+
job3.id = 1
66+
67+
// job4 gets the Infinity as it's id
68+
const job4 = () => {
69+
calls.push('job4')
70+
}
71+
72+
queueJob(job1)
73+
74+
expect(calls).toEqual([])
75+
await nextTick()
76+
expect(calls).toEqual(['job1', 'job3', 'job2', 'job4'])
77+
})
78+
4779
it('should dedupe queued jobs', async () => {
4880
const calls: string[] = []
4981
const job1 = () => {

packages/runtime-core/src/scheduler.ts

+25-1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,25 @@ export function nextTick(
5757
return fn ? p.then(this ? fn.bind(this) : fn) : p
5858
}
5959

60+
// #2768
61+
// Use binary-search to find a suitable position in the queue,
62+
// so that the queue maintains the increasing order of job's id,
63+
// which can prevent the job from being skipped and also can avoid repeated patching.
64+
function findInsertionIndex(job: SchedulerJob) {
65+
// the start index should be `flushIndex + 1`
66+
let start = flushIndex + 1
67+
let end = queue.length
68+
const jobId = getId(job)
69+
70+
while (start < end) {
71+
const middle = (start + end) >>> 1
72+
const middleJobId = getId(queue[middle])
73+
middleJobId < jobId ? (start = middle + 1) : (end = middle)
74+
}
75+
76+
return start
77+
}
78+
6079
export function queueJob(job: SchedulerJob) {
6180
// the dedupe search uses the startIndex argument of Array.includes()
6281
// by default the search index includes the current job that is being run
@@ -72,7 +91,12 @@ export function queueJob(job: SchedulerJob) {
7291
)) &&
7392
job !== currentPreFlushParentJob
7493
) {
75-
queue.push(job)
94+
const pos = findInsertionIndex(job)
95+
if (pos > -1) {
96+
queue.splice(pos, 0, job)
97+
} else {
98+
queue.push(job)
99+
}
76100
queueFlush()
77101
}
78102
}

0 commit comments

Comments
 (0)