refactor(scheduler): use bitwise flags for scheduler jobs + move scheduler into reactivity
related: https://github.com/vuejs/core/pull/10407
This commit is contained in:
parent
174118ae40
commit
db4040d13a
|
@ -1,8 +1,9 @@
|
||||||
import type { Scheduler, SchedulerJob } from '../src/baseWatch'
|
|
||||||
import {
|
import {
|
||||||
BaseWatchErrorCodes,
|
BaseWatchErrorCodes,
|
||||||
EffectScope,
|
EffectScope,
|
||||||
type Ref,
|
type Ref,
|
||||||
|
type SchedulerJob,
|
||||||
|
type WatchScheduler,
|
||||||
baseWatch,
|
baseWatch,
|
||||||
onEffectCleanup,
|
onEffectCleanup,
|
||||||
ref,
|
ref,
|
||||||
|
@ -15,9 +16,13 @@ let isFlushPending = false
|
||||||
const resolvedPromise = /*#__PURE__*/ Promise.resolve() as Promise<any>
|
const resolvedPromise = /*#__PURE__*/ Promise.resolve() as Promise<any>
|
||||||
const nextTick = (fn?: () => any) =>
|
const nextTick = (fn?: () => any) =>
|
||||||
fn ? resolvedPromise.then(fn) : resolvedPromise
|
fn ? resolvedPromise.then(fn) : resolvedPromise
|
||||||
const scheduler: Scheduler = job => {
|
const scheduler: WatchScheduler = (job, effect, immediateFirstRun, hasCb) => {
|
||||||
queue.push(job)
|
if (immediateFirstRun) {
|
||||||
flushJobs()
|
!hasCb && effect.run()
|
||||||
|
} else {
|
||||||
|
queue.push(() => job(immediateFirstRun))
|
||||||
|
flushJobs()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const flushJobs = () => {
|
const flushJobs = () => {
|
||||||
if (isFlushPending) return
|
if (isFlushPending) return
|
||||||
|
@ -214,7 +219,11 @@ describe('baseWatch', () => {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(effectCalls).toEqual([])
|
expect(effectCalls).toEqual([
|
||||||
|
'before effect running',
|
||||||
|
'effect',
|
||||||
|
'effect ran',
|
||||||
|
])
|
||||||
expect(watchCalls).toEqual([])
|
expect(watchCalls).toEqual([])
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(effectCalls).toEqual([
|
expect(effectCalls).toEqual([
|
||||||
|
|
|
@ -22,7 +22,7 @@ import {
|
||||||
} from './effect'
|
} from './effect'
|
||||||
import { isReactive, isShallow } from './reactive'
|
import { isReactive, isShallow } from './reactive'
|
||||||
import { type Ref, isRef } from './ref'
|
import { type Ref, isRef } from './ref'
|
||||||
import { getCurrentScope } from './effectScope'
|
import { type SchedulerJob, SchedulerJobFlags } from './scheduler'
|
||||||
|
|
||||||
// These errors were transferred from `packages/runtime-core/src/errorHandling.ts`
|
// These errors were transferred from `packages/runtime-core/src/errorHandling.ts`
|
||||||
// along with baseWatch to maintain code compatibility. Hence,
|
// along with baseWatch to maintain code compatibility. Hence,
|
||||||
|
@ -33,32 +33,6 @@ export enum BaseWatchErrorCodes {
|
||||||
WATCH_CLEANUP,
|
WATCH_CLEANUP,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO move to a scheduler package
|
|
||||||
export interface SchedulerJob extends Function {
|
|
||||||
id?: number
|
|
||||||
// TODO refactor these boolean flags to a single bitwise flag
|
|
||||||
pre?: boolean
|
|
||||||
active?: boolean
|
|
||||||
computed?: boolean
|
|
||||||
queued?: boolean
|
|
||||||
/**
|
|
||||||
* Indicates whether the effect is allowed to recursively trigger itself
|
|
||||||
* when managed by the scheduler.
|
|
||||||
*
|
|
||||||
* By default, a job cannot trigger itself because some built-in method calls,
|
|
||||||
* e.g. Array.prototype.push actually performs reads as well (#1740) which
|
|
||||||
* can lead to confusing infinite loops.
|
|
||||||
* The allowed cases are component update functions and watch callbacks.
|
|
||||||
* Component update functions may update child component props, which in turn
|
|
||||||
* trigger flush: "pre" watch callbacks that mutates state that the parent
|
|
||||||
* relies on (#1801). Watch callbacks doesn't track its dependencies so if it
|
|
||||||
* triggers itself again, it's likely intentional and it is the user's
|
|
||||||
* responsibility to perform recursive state mutation that eventually
|
|
||||||
* stabilizes (#1727).
|
|
||||||
*/
|
|
||||||
allowRecurse?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
type WatchEffect = (onCleanup: OnCleanup) => void
|
type WatchEffect = (onCleanup: OnCleanup) => void
|
||||||
type WatchSource<T = any> = Ref<T> | ComputedRef<T> | (() => T)
|
type WatchSource<T = any> = Ref<T> | ComputedRef<T> | (() => T)
|
||||||
type WatchCallback<V = any, OV = any> = (
|
type WatchCallback<V = any, OV = any> = (
|
||||||
|
@ -254,8 +228,11 @@ export function baseWatch(
|
||||||
let oldValue: any = isMultiSource
|
let oldValue: any = isMultiSource
|
||||||
? new Array((source as []).length).fill(INITIAL_WATCHER_VALUE)
|
? new Array((source as []).length).fill(INITIAL_WATCHER_VALUE)
|
||||||
: INITIAL_WATCHER_VALUE
|
: INITIAL_WATCHER_VALUE
|
||||||
const job: SchedulerJob = () => {
|
const job: SchedulerJob = (immediateFirstRun?: boolean) => {
|
||||||
if (!effect.active || !effect.dirty) {
|
if (
|
||||||
|
!(effect.flags & EffectFlags.ACTIVE) ||
|
||||||
|
(!effect.dirty && !immediateFirstRun)
|
||||||
|
) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (cb) {
|
if (cb) {
|
||||||
|
@ -310,11 +287,10 @@ export function baseWatch(
|
||||||
|
|
||||||
// important: mark the job as a watcher callback so that scheduler knows
|
// important: mark the job as a watcher callback so that scheduler knows
|
||||||
// it is allowed to self-trigger (#1727)
|
// it is allowed to self-trigger (#1727)
|
||||||
job.allowRecurse = !!cb
|
if (cb) job.flags! |= SchedulerJobFlags.ALLOW_RECURSE
|
||||||
|
|
||||||
let effectScheduler: EffectScheduler = () => scheduler(job, effect, false)
|
effect = new ReactiveEffect(getter)
|
||||||
|
effect.scheduler = () => scheduler(job, effect, false, !!cb)
|
||||||
effect = new ReactiveEffect(getter, NOOP, effectScheduler, scope)
|
|
||||||
|
|
||||||
cleanup = effect.onStop = () => {
|
cleanup = effect.onStop = () => {
|
||||||
const cleanups = cleanupMap.get(effect)
|
const cleanups = cleanupMap.get(effect)
|
||||||
|
@ -337,13 +313,14 @@ export function baseWatch(
|
||||||
|
|
||||||
// initial run
|
// initial run
|
||||||
if (cb) {
|
if (cb) {
|
||||||
|
scheduler(job, effect, true, !!cb)
|
||||||
if (immediate) {
|
if (immediate) {
|
||||||
job()
|
job(true)
|
||||||
} else {
|
} else {
|
||||||
oldValue = effect.run()
|
oldValue = effect.run()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
scheduler(job, effect, true)
|
scheduler(job, effect, true, !!cb)
|
||||||
}
|
}
|
||||||
|
|
||||||
return effect
|
return effect
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
import { SchedulerJobFlags } from '@vue/reactivity'
|
||||||
import {
|
import {
|
||||||
type SchedulerJob,
|
type SchedulerJob,
|
||||||
SchedulerJobFlags,
|
|
||||||
flushPostFlushCbs,
|
flushPostFlushCbs,
|
||||||
flushPreFlushCbs,
|
flushPreFlushCbs,
|
||||||
invalidateJob,
|
invalidateJob,
|
||||||
|
|
|
@ -14,12 +14,11 @@ import {
|
||||||
} from '../vnode'
|
} from '../vnode'
|
||||||
import { warn } from '../warning'
|
import { warn } from '../warning'
|
||||||
import { isKeepAlive } from './KeepAlive'
|
import { isKeepAlive } from './KeepAlive'
|
||||||
import { toRaw } from '@vue/reactivity'
|
import { SchedulerJobFlags, toRaw } from '@vue/reactivity'
|
||||||
import { ErrorCodes, callWithAsyncErrorHandling } from '../errorHandling'
|
import { ErrorCodes, callWithAsyncErrorHandling } from '../errorHandling'
|
||||||
import { PatchFlags, ShapeFlags, isArray } from '@vue/shared'
|
import { PatchFlags, ShapeFlags, isArray } from '@vue/shared'
|
||||||
import { onBeforeUnmount, onMounted } from '../apiLifecycle'
|
import { onBeforeUnmount, onMounted } from '../apiLifecycle'
|
||||||
import type { RendererElement } from '../renderer'
|
import type { RendererElement } from '../renderer'
|
||||||
import { SchedulerJobFlags } from '../scheduler'
|
|
||||||
|
|
||||||
type Hook<T = () => void> = T | T[]
|
type Hook<T = () => void> = T | T[]
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,6 @@ import {
|
||||||
import {
|
import {
|
||||||
type SchedulerFactory,
|
type SchedulerFactory,
|
||||||
type SchedulerJob,
|
type SchedulerJob,
|
||||||
SchedulerJobFlags,
|
|
||||||
flushPostFlushCbs,
|
flushPostFlushCbs,
|
||||||
flushPreFlushCbs,
|
flushPreFlushCbs,
|
||||||
invalidateJob,
|
invalidateJob,
|
||||||
|
@ -50,6 +49,7 @@ import {
|
||||||
import {
|
import {
|
||||||
EffectFlags,
|
EffectFlags,
|
||||||
ReactiveEffect,
|
ReactiveEffect,
|
||||||
|
SchedulerJobFlags,
|
||||||
pauseTracking,
|
pauseTracking,
|
||||||
resetTracking,
|
resetTracking,
|
||||||
} from '@vue/reactivity'
|
} from '@vue/reactivity'
|
||||||
|
@ -289,14 +289,14 @@ export const queuePostRenderEffect = __FEATURE_SUSPENSE__
|
||||||
: queuePostFlushCb
|
: queuePostFlushCb
|
||||||
|
|
||||||
export const createPostRenderScheduler: SchedulerFactory =
|
export const createPostRenderScheduler: SchedulerFactory =
|
||||||
instance => (job, effect, isInit) => {
|
instance => (job, effect, immediateFirstRun, hasCb) => {
|
||||||
if (isInit) {
|
if (!immediateFirstRun) {
|
||||||
|
queuePostRenderEffect(job, instance && instance.suspense)
|
||||||
|
} else if (!hasCb) {
|
||||||
queuePostRenderEffect(
|
queuePostRenderEffect(
|
||||||
effect.run.bind(effect),
|
effect.run.bind(effect),
|
||||||
instance && instance.suspense,
|
instance && instance.suspense,
|
||||||
)
|
)
|
||||||
} else {
|
|
||||||
queuePostRenderEffect(job, instance && instance.suspense)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,37 +1,14 @@
|
||||||
import { ErrorCodes, callWithErrorHandling, handleError } from './errorHandling'
|
import { ErrorCodes, callWithErrorHandling, handleError } from './errorHandling'
|
||||||
import { type Awaited, NOOP, isArray } from '@vue/shared'
|
import { type Awaited, NOOP, isArray } from '@vue/shared'
|
||||||
import { type ComponentInternalInstance, getComponentName } from './component'
|
import { type ComponentInternalInstance, getComponentName } from './component'
|
||||||
import type { Scheduler } from '@vue/reactivity'
|
import {
|
||||||
|
type SchedulerJob as BaseSchedulerJob,
|
||||||
|
EffectFlags,
|
||||||
|
SchedulerJobFlags,
|
||||||
|
type WatchScheduler,
|
||||||
|
} from '@vue/reactivity'
|
||||||
|
|
||||||
export enum SchedulerJobFlags {
|
export interface SchedulerJob extends BaseSchedulerJob {
|
||||||
QUEUED = 1 << 0,
|
|
||||||
PRE = 1 << 1,
|
|
||||||
/**
|
|
||||||
* Indicates whether the effect is allowed to recursively trigger itself
|
|
||||||
* when managed by the scheduler.
|
|
||||||
*
|
|
||||||
* By default, a job cannot trigger itself because some built-in method calls,
|
|
||||||
* e.g. Array.prototype.push actually performs reads as well (#1740) which
|
|
||||||
* can lead to confusing infinite loops.
|
|
||||||
* The allowed cases are component update functions and watch callbacks.
|
|
||||||
* Component update functions may update child component props, which in turn
|
|
||||||
* trigger flush: "pre" watch callbacks that mutates state that the parent
|
|
||||||
* relies on (#1801). Watch callbacks doesn't track its dependencies so if it
|
|
||||||
* triggers itself again, it's likely intentional and it is the user's
|
|
||||||
* responsibility to perform recursive state mutation that eventually
|
|
||||||
* stabilizes (#1727).
|
|
||||||
*/
|
|
||||||
ALLOW_RECURSE = 1 << 2,
|
|
||||||
DISPOSED = 1 << 3,
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SchedulerJob extends Function {
|
|
||||||
id?: number
|
|
||||||
/**
|
|
||||||
* flags can technically be undefined, but it can still be used in bitwise
|
|
||||||
* operations just like 0.
|
|
||||||
*/
|
|
||||||
flags?: SchedulerJobFlags
|
|
||||||
/**
|
/**
|
||||||
* Attached by renderer.ts when setting up a component's render effect
|
* Attached by renderer.ts when setting up a component's render effect
|
||||||
* Used to obtain component information when reporting max recursive updates.
|
* Used to obtain component information when reporting max recursive updates.
|
||||||
|
@ -301,24 +278,25 @@ function checkRecursiveUpdates(seen: CountMap, fn: SchedulerJob) {
|
||||||
|
|
||||||
export type SchedulerFactory = (
|
export type SchedulerFactory = (
|
||||||
instance: ComponentInternalInstance | null,
|
instance: ComponentInternalInstance | null,
|
||||||
) => Scheduler
|
) => WatchScheduler
|
||||||
|
|
||||||
export const createSyncScheduler: SchedulerFactory =
|
export const createSyncScheduler: SchedulerFactory =
|
||||||
instance => (job, effect, isInit) => {
|
instance => (job, effect, immediateFirstRun, hasCb) => {
|
||||||
if (isInit) {
|
if (immediateFirstRun) {
|
||||||
effect.run()
|
effect.flags |= EffectFlags.NO_BATCH
|
||||||
|
if (!hasCb) effect.run()
|
||||||
} else {
|
} else {
|
||||||
job()
|
job()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createPreScheduler: SchedulerFactory =
|
export const createPreScheduler: SchedulerFactory =
|
||||||
instance => (job, effect, isInit) => {
|
instance => (job, effect, immediateFirstRun, hasCb) => {
|
||||||
if (isInit) {
|
if (!immediateFirstRun) {
|
||||||
effect.run()
|
job.flags! |= SchedulerJobFlags.PRE
|
||||||
} else {
|
|
||||||
job.pre = true
|
|
||||||
if (instance) job.id = instance.uid
|
if (instance) job.id = instance.uid
|
||||||
queueJob(job)
|
queueJob(job)
|
||||||
|
} else if (!hasCb) {
|
||||||
|
effect.run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,11 +38,24 @@ describe('renderWatch', () => {
|
||||||
renderEffect(() => {
|
renderEffect(() => {
|
||||||
dummy = source.value
|
dummy = source.value
|
||||||
})
|
})
|
||||||
|
expect(dummy).toBe(0)
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(dummy).toBe(0)
|
expect(dummy).toBe(0)
|
||||||
|
|
||||||
source.value++
|
source.value++
|
||||||
|
expect(dummy).toBe(0)
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(dummy).toBe(1)
|
expect(dummy).toBe(1)
|
||||||
|
|
||||||
|
source.value++
|
||||||
|
expect(dummy).toBe(1)
|
||||||
|
await nextTick()
|
||||||
|
expect(dummy).toBe(2)
|
||||||
|
|
||||||
|
source.value++
|
||||||
|
expect(dummy).toBe(2)
|
||||||
|
await nextTick()
|
||||||
|
expect(dummy).toBe(3)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('watch', async () => {
|
test('watch', async () => {
|
||||||
|
@ -53,9 +66,16 @@ describe('renderWatch', () => {
|
||||||
})
|
})
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(dummy).toBe(undefined)
|
expect(dummy).toBe(undefined)
|
||||||
|
|
||||||
source.value++
|
source.value++
|
||||||
|
expect(dummy).toBe(undefined)
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(dummy).toBe(1)
|
expect(dummy).toBe(1)
|
||||||
|
|
||||||
|
source.value++
|
||||||
|
expect(dummy).toBe(1)
|
||||||
|
await nextTick()
|
||||||
|
expect(dummy).toBe(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should run with the scheduling order', async () => {
|
test('should run with the scheduling order', async () => {
|
||||||
|
@ -136,6 +156,28 @@ describe('renderWatch', () => {
|
||||||
'post 1',
|
'post 1',
|
||||||
'updated 1',
|
'updated 1',
|
||||||
])
|
])
|
||||||
|
calls.length = 0
|
||||||
|
|
||||||
|
// Update
|
||||||
|
changeRender()
|
||||||
|
change()
|
||||||
|
|
||||||
|
expect(calls).toEqual(['sync cleanup 1', 'sync 2'])
|
||||||
|
calls.length = 0
|
||||||
|
|
||||||
|
await nextTick()
|
||||||
|
expect(calls).toEqual([
|
||||||
|
'pre cleanup 1',
|
||||||
|
'pre 2',
|
||||||
|
'beforeUpdate 2',
|
||||||
|
'renderEffect cleanup 1',
|
||||||
|
'renderEffect 2',
|
||||||
|
'renderWatch cleanup 1',
|
||||||
|
'renderWatch 2',
|
||||||
|
'post cleanup 1',
|
||||||
|
'post 2',
|
||||||
|
'updated 2',
|
||||||
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
test('errors should include the execution location with beforeUpdate hook', async () => {
|
test('errors should include the execution location with beforeUpdate hook', async () => {
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
import type { Scheduler, SchedulerJob } from '@vue/reactivity'
|
import {
|
||||||
|
EffectFlags,
|
||||||
|
type SchedulerJob,
|
||||||
|
SchedulerJobFlags,
|
||||||
|
type WatchScheduler,
|
||||||
|
} from '@vue/reactivity'
|
||||||
import type { ComponentInternalInstance } from './component'
|
import type { ComponentInternalInstance } from './component'
|
||||||
import { isArray } from '@vue/shared'
|
import { isArray } from '@vue/shared'
|
||||||
|
|
||||||
|
@ -28,19 +33,21 @@ const resolvedPromise = /*#__PURE__*/ Promise.resolve() as Promise<any>
|
||||||
let currentFlushPromise: Promise<void> | null = null
|
let currentFlushPromise: Promise<void> | null = null
|
||||||
|
|
||||||
function queueJob(job: SchedulerJob) {
|
function queueJob(job: SchedulerJob) {
|
||||||
if (!job.queued) {
|
if (!(job.flags! & SchedulerJobFlags.QUEUED)) {
|
||||||
if (job.id == null) {
|
if (job.id == null) {
|
||||||
queue.push(job)
|
queue.push(job)
|
||||||
} else {
|
} else if (
|
||||||
// fast path when the job id is larger than the tail
|
// fast path when the job id is larger than the tail
|
||||||
if (!job.pre && job.id >= (queue[queue.length - 1]?.id || 0)) {
|
!(job.flags! & SchedulerJobFlags.PRE) &&
|
||||||
queue.push(job)
|
job.id >= (queue[queue.length - 1]?.id || 0)
|
||||||
} else {
|
) {
|
||||||
queue.splice(findInsertionIndex(job.id), 0, job)
|
queue.push(job)
|
||||||
}
|
} else {
|
||||||
|
queue.splice(findInsertionIndex(job.id), 0, job)
|
||||||
}
|
}
|
||||||
if (!job.allowRecurse) {
|
|
||||||
job.queued = true
|
if (!(job.flags! & SchedulerJobFlags.ALLOW_RECURSE)) {
|
||||||
|
job.flags! |= SchedulerJobFlags.QUEUED
|
||||||
}
|
}
|
||||||
queueFlush()
|
queueFlush()
|
||||||
}
|
}
|
||||||
|
@ -48,10 +55,10 @@ function queueJob(job: SchedulerJob) {
|
||||||
|
|
||||||
export function queuePostRenderEffect(cb: SchedulerJobs) {
|
export function queuePostRenderEffect(cb: SchedulerJobs) {
|
||||||
if (!isArray(cb)) {
|
if (!isArray(cb)) {
|
||||||
if (!cb.queued) {
|
if (!(cb.flags! & SchedulerJobFlags.QUEUED)) {
|
||||||
pendingPostFlushCbs.push(cb)
|
pendingPostFlushCbs.push(cb)
|
||||||
if (!cb.allowRecurse) {
|
if (!(cb.flags! & SchedulerJobFlags.ALLOW_RECURSE)) {
|
||||||
cb.queued = true
|
cb.flags! |= SchedulerJobFlags.QUEUED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -92,7 +99,7 @@ export function flushPostFlushCbs() {
|
||||||
postFlushIndex++
|
postFlushIndex++
|
||||||
) {
|
) {
|
||||||
activePostFlushCbs[postFlushIndex]()
|
activePostFlushCbs[postFlushIndex]()
|
||||||
activePostFlushCbs[postFlushIndex].queued = false
|
activePostFlushCbs[postFlushIndex].flags! &= ~SchedulerJobFlags.QUEUED
|
||||||
}
|
}
|
||||||
activePostFlushCbs = null
|
activePostFlushCbs = null
|
||||||
postFlushIndex = 0
|
postFlushIndex = 0
|
||||||
|
@ -114,8 +121,8 @@ function flushJobs() {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (let i = 0; i < queue!.length; i++) {
|
for (let i = 0; i < queue!.length; i++) {
|
||||||
queue![i]()
|
queue[i]()
|
||||||
queue![i].queued = false
|
queue[i].flags! &= ~SchedulerJobFlags.QUEUED
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
flushIndex = 0
|
flushIndex = 0
|
||||||
|
@ -154,7 +161,10 @@ function findInsertionIndex(id: number) {
|
||||||
const middle = (start + end) >>> 1
|
const middle = (start + end) >>> 1
|
||||||
const middleJob = queue[middle]
|
const middleJob = queue[middle]
|
||||||
const middleJobId = getId(middleJob)
|
const middleJobId = getId(middleJob)
|
||||||
if (middleJobId < id || (middleJobId === id && middleJob.pre)) {
|
if (
|
||||||
|
middleJobId < id ||
|
||||||
|
(middleJobId === id && middleJob.flags! & SchedulerJobFlags.PRE)
|
||||||
|
) {
|
||||||
start = middle + 1
|
start = middle + 1
|
||||||
} else {
|
} else {
|
||||||
end = middle
|
end = middle
|
||||||
|
@ -170,52 +180,54 @@ const getId = (job: SchedulerJob): number =>
|
||||||
const comparator = (a: SchedulerJob, b: SchedulerJob): number => {
|
const comparator = (a: SchedulerJob, b: SchedulerJob): number => {
|
||||||
const diff = getId(a) - getId(b)
|
const diff = getId(a) - getId(b)
|
||||||
if (diff === 0) {
|
if (diff === 0) {
|
||||||
if (a.pre && !b.pre) return -1
|
const isAPre = a.flags! & SchedulerJobFlags.PRE
|
||||||
if (b.pre && !a.pre) return 1
|
const isBPre = b.flags! & SchedulerJobFlags.PRE
|
||||||
|
if (isAPre && !isBPre) return -1
|
||||||
|
if (isBPre && !isAPre) return 1
|
||||||
}
|
}
|
||||||
return diff
|
return diff
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SchedulerFactory = (
|
export type SchedulerFactory = (
|
||||||
instance: ComponentInternalInstance | null,
|
instance: ComponentInternalInstance | null,
|
||||||
) => Scheduler
|
) => WatchScheduler
|
||||||
|
|
||||||
export const createVaporSyncScheduler: SchedulerFactory =
|
export const createVaporSyncScheduler: SchedulerFactory =
|
||||||
() => (job, effect, isInit) => {
|
instance => (job, effect, immediateFirstRun, hasCb) => {
|
||||||
if (isInit) {
|
if (immediateFirstRun) {
|
||||||
effect.run()
|
effect.flags |= EffectFlags.NO_BATCH
|
||||||
|
if (!hasCb) effect.run()
|
||||||
} else {
|
} else {
|
||||||
job()
|
job()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createVaporPreScheduler: SchedulerFactory =
|
export const createVaporPreScheduler: SchedulerFactory =
|
||||||
instance => (job, effect, isInit) => {
|
instance => (job, effect, immediateFirstRun, hasCb) => {
|
||||||
if (isInit) {
|
if (!immediateFirstRun) {
|
||||||
effect.run()
|
job.flags! |= SchedulerJobFlags.PRE
|
||||||
} else {
|
|
||||||
job.pre = true
|
|
||||||
if (instance) job.id = instance.uid
|
if (instance) job.id = instance.uid
|
||||||
queueJob(job)
|
queueJob(job)
|
||||||
|
} else if (!hasCb) {
|
||||||
|
effect.run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createVaporRenderingScheduler: SchedulerFactory =
|
export const createVaporRenderingScheduler: SchedulerFactory =
|
||||||
instance => (job, effect, isInit) => {
|
instance => (job, effect, immediateFirstRun, hasCb) => {
|
||||||
if (isInit) {
|
if (!immediateFirstRun) {
|
||||||
effect.run()
|
|
||||||
} else {
|
|
||||||
job.pre = false
|
|
||||||
if (instance) job.id = instance.uid
|
if (instance) job.id = instance.uid
|
||||||
queueJob(job)
|
queueJob(job)
|
||||||
|
} else if (!hasCb) {
|
||||||
|
effect.run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createVaporPostScheduler: SchedulerFactory =
|
export const createVaporPostScheduler: SchedulerFactory =
|
||||||
() => (job, effect, isInit) => {
|
instance => (job, effect, immediateFirstRun, hasCb) => {
|
||||||
if (isInit) {
|
if (!immediateFirstRun) {
|
||||||
queuePostRenderEffect(effect.run.bind(effect))
|
|
||||||
} else {
|
|
||||||
queuePostRenderEffect(job)
|
queuePostRenderEffect(job)
|
||||||
|
} else if (!hasCb) {
|
||||||
|
queuePostRenderEffect(effect.run.bind(effect))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue