From 4a01f906487e0252cc0cd8b7a2aafd8dbb300ef2 Mon Sep 17 00:00:00 2001 From: HcySunYang Date: Sun, 7 Feb 2021 15:38:40 +0800 Subject: [PATCH 1/3] fix(scheduler): should insert jobs in ascending order of job's id when flushing --- .../runtime-core/__tests__/scheduler.spec.ts | 32 +++++++++++++++++++ packages/runtime-core/src/scheduler.ts | 20 +++++++++++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/packages/runtime-core/__tests__/scheduler.spec.ts b/packages/runtime-core/__tests__/scheduler.spec.ts index 39f4de981a4..3d95be5edb3 100644 --- a/packages/runtime-core/__tests__/scheduler.spec.ts +++ b/packages/runtime-core/__tests__/scheduler.spec.ts @@ -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 = () => { diff --git a/packages/runtime-core/src/scheduler.ts b/packages/runtime-core/src/scheduler.ts index e2781f97e4d..5aefc670373 100644 --- a/packages/runtime-core/src/scheduler.ts +++ b/packages/runtime-core/src/scheduler.ts @@ -57,6 +57,19 @@ export function nextTick( return fn ? p.then(this ? fn.bind(this) : fn) : p } +const findInsertionIndex = (job: SchedulerJob) => { + 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 @@ -72,7 +85,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() } } From 92891fa4776c171231ddc4f62ddd745916ce2916 Mon Sep 17 00:00:00 2001 From: HcySunYang Date: Sun, 7 Feb 2021 18:07:00 +0800 Subject: [PATCH 2/3] chore: comments --- packages/runtime-core/src/scheduler.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/runtime-core/src/scheduler.ts b/packages/runtime-core/src/scheduler.ts index 5aefc670373..315e76efc41 100644 --- a/packages/runtime-core/src/scheduler.ts +++ b/packages/runtime-core/src/scheduler.ts @@ -57,7 +57,12 @@ 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. const findInsertionIndex = (job: SchedulerJob) => { + // the start index should be `flushIndex + 1` let start = flushIndex + 1 let end = queue.length const jobId = getId(job) From fceff568b67af4dab03adf13715f62fa99f1525d Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 25 Feb 2021 09:33:23 -0500 Subject: [PATCH 3/3] Update scheduler.ts --- packages/runtime-core/src/scheduler.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/runtime-core/src/scheduler.ts b/packages/runtime-core/src/scheduler.ts index 315e76efc41..6feb2603e76 100644 --- a/packages/runtime-core/src/scheduler.ts +++ b/packages/runtime-core/src/scheduler.ts @@ -61,7 +61,7 @@ export function nextTick( // 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. -const findInsertionIndex = (job: SchedulerJob) => { +function findInsertionIndex(job: SchedulerJob) { // the start index should be `flushIndex + 1` let start = flushIndex + 1 let end = queue.length @@ -75,6 +75,7 @@ const findInsertionIndex = (job: SchedulerJob) => { 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