Просмотр исходного кода

fix(scheduler): ensure updates are always inserted in ascending id order (#3184)

fix #2768, fix #2829
HcySunYang 5 лет назад
Родитель
Сommit
45fae9d308
2 измененных файлов с 57 добавлено и 1 удалено
  1. 32 0
      packages/runtime-core/__tests__/scheduler.spec.ts
  2. 25 1
      packages/runtime-core/src/scheduler.ts

+ 32 - 0
packages/runtime-core/__tests__/scheduler.spec.ts

@@ -44,6 +44,38 @@ describe('scheduler', () => {
       expect(calls).toEqual(['job1', 'job2'])
       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 () => {
     it('should dedupe queued jobs', async () => {
       const calls: string[] = []
       const calls: string[] = []
       const job1 = () => {
       const job1 = () => {

+ 25 - 1
packages/runtime-core/src/scheduler.ts

@@ -57,6 +57,25 @@ export function nextTick(
   return fn ? p.then(this ? fn.bind(this) : fn) : p
   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) {
 export function queueJob(job: SchedulerJob) {
   // the dedupe search uses the startIndex argument of Array.includes()
   // the dedupe search uses the startIndex argument of Array.includes()
   // by default the search index includes the current job that is being run
   // by default the search index includes the current job that is being run
@@ -72,7 +91,12 @@ export function queueJob(job: SchedulerJob) {
       )) &&
       )) &&
     job !== currentPreFlushParentJob
     job !== currentPreFlushParentJob
   ) {
   ) {
-    queue.push(job)
+    const pos = findInsertionIndex(job)
+    if (pos > -1) {
+      queue.splice(pos, 0, job)
+    } else {
+      queue.push(job)
+    }
     queueFlush()
     queueFlush()
   }
   }
 }
 }