【vue3源码】六、scheduler

在前文剖析watch的过程中,咱们晓得在effect的调度器中会将job推入不同的工作队列,以在不同机会执行job函数。本文将深入分析job的执行机会。

watch中,会依据flushscheduler进行不同解决。如果flushsync,代表同步,那么scheduler就是job,在依赖触发时,会间接执行job;如果schedulerpost,在依赖触发时,会调用一个queuePostRenderEffect函数;而默认状况,在依赖触发时,会依据以后有无组件实例,进行不同操作。

if (flush === 'sync') {    scheduler = job as any  } else if (flush === 'post') {    scheduler = () => queuePostRenderEffect(job, instance && instance.suspense)  } else {    scheduler = () => {      if (!instance || instance.isMounted) {        queuePreFlushCb(job)      } else {        job()      }    }  }

queuePostRenderEffect

export const queuePostRenderEffect = __FEATURE_SUSPENSE__  ? queueEffectWithSuspense  : queuePostFlushCb

如果开启了__FEATURE_SUSPENSE__了,queuePostRenderEffectqueueEffectWithSuspense,否则是queuePostFlushCb。这里为了不便了解,咱们只看queuePostFlushCb

queuePostFlushCb

export function queuePostFlushCb(cb: SchedulerJobs) {  queueCb(cb, activePostFlushCbs, pendingPostFlushCbs, postFlushIndex)}

queuePostFlushCb接管一个SchedulerJobs类型cb参数,对于SchedulerJobs的定义:

export interface SchedulerJob extends Function {  // 一个标识  id?: number  // 是否为激活状态  active?: boolean  computed?: boolean  allowRecurse?: boolean  ownerInstance?: ComponentInternalInstance}export type SchedulerJobs = SchedulerJob | SchedulerJob[]

queuePostFlushCb外部调用了一个queueCb办法,并传入了四个变量,其中第一个是queuePostFlushCb的参数,而后三个是全局变量

const pendingPostFlushCbs: SchedulerJob[] = []let activePostFlushCbs: SchedulerJob[] | null = nulllet postFlushIndex = 0

接着看queueCb

queueCb

function queueCb(  cb: SchedulerJobs,  activeQueue: SchedulerJob[] | null,  pendingQueue: SchedulerJob[],  index: number) {  if (!isArray(cb)) {    if (      !activeQueue ||      !activeQueue.includes(cb, cb.allowRecurse ? index + 1 : index)    ) {      pendingQueue.push(cb)    }  } else {    pendingQueue.push(...cb)  }  queueFlush()}

queueCb函数接管四个参数:cbactiveQueue(激活的队列)、pendingQueue(期待中的队列)、index(一个索引),

如果cb是数组,会将cb解构放入pendingQueue中;否则判断是否传入activeQueueactiveQueue中是否曾经存在cb(如果cb.allowRecursetrue,从index+1处开始寻找,否则从index处开始寻找)。最初执行一个queueFlush函数。

queueFlush

// 以后是否有正在执行的工作let isFlushing = false// 以后是否有正在期待执行的工作let isFlushPending = falsefunction queueFlush() {  if (!isFlushing && !isFlushPending) {    isFlushPending = true    // 正在执行的promise    currentFlushPromise = resolvedPromise.then(flushJobs)  }}

queueFlush中的逻辑比较简单。如果没有正在执行的工作并且也没有正在期待中的工作,则将isFlushPending置为true,同时将flushJobs放入一个微工作队列。

flushJobs

function flushJobs(seen?: CountMap) {  // 将isFlushPending置为false,因为曾经进入执行工作中的状态  isFlushPending = false  // 正在执行工作  isFlushing = true  if (__DEV__) {    seen = seen || new Map()  }  // 执行pendingPreFlushCbs中的工作  flushPreFlushCbs(seen)  // 将queue按job.id升序  queue.sort((a, b) => getId(a) - getId(b))  const check = __DEV__    ? (job: SchedulerJob) => checkRecursiveUpdates(seen!, job)    : NOOP  try {    // 执行queue中的工作    for (flushIndex = 0; flushIndex < queue.length; flushIndex++) {      const job = queue[flushIndex]      if (job && job.active !== false) {        if (__DEV__ && check(job)) {          continue        }        callWithErrorHandling(job, null, ErrorCodes.SCHEDULER)      }    }  } finally {    // 重置flushIndex    flushIndex = 0    // 清空queue    queue.length = 0    // 执行pendingPostFlushCbs中的工作    flushPostFlushCbs(seen)    // 以后没有正在执行的工作    isFlushing = false    currentFlushPromise = null    // 执行残余工作    if (      queue.length ||      pendingPreFlushCbs.length ||      pendingPostFlushCbs.length    ) {      flushJobs(seen)    }  }}

flushJobs中会顺次执行pendingPreFlushCbsqueuependingPostFlushCbs中的工作。

再来看queuePreFlushCb

queuePreFlushCb

const pendingPreFlushCbs: SchedulerJob[] = []let activePreFlushCbs: SchedulerJob[] | null = nulllet preFlushIndex = 0export function queuePreFlushCb(cb: SchedulerJob) {  queueCb(cb, activePreFlushCbs, pendingPreFlushCbs, preFlushIndex)}

queuePreFlushCb办法会将cb放入一个pendingPreFlushCbs数组。

通过前文咱们晓得最终pendingPreFlushCbsqueuependingPostFlushCbs会按程序顺次执行,pendingPreFlushCbs中保留的是flush===pre时的jobpendingPostFlushCbs中保留的是flush===post时的job。那么queue中是什么呢?queue中保留的组件的更新函数。

const effect = (instance.effect = new ReactiveEffect(  componentUpdateFn,  () => queueJob(instance.update),  instance.scope))

queueJob

export function queueJob(job: SchedulerJob) {  // queue.length为0或queue中不存在job,并且job不等于currentPreFlushParentJob  if (    (!queue.length ||      !queue.includes(        job,        isFlushing && job.allowRecurse ? flushIndex + 1 : flushIndex      )) &&    job !== currentPreFlushParentJob  ) {    if (job.id == null) {      queue.push(job)    } else {      // 替换反复id的job      queue.splice(findInsertionIndex(job.id), 0, job)    }    queueFlush()  }}

nextTick

export function nextTick<T = void>(  this: T,  fn?: (this: T) => void): Promise<void> {  const p = currentFlushPromise || resolvedPromise  // 将fn退出到一个微工作队列,它会在p执行完之后执行  return fn ? p.then(this ? fn.bind(this) : fn) : p}

在每次向pendingPreFlushCbsqueuependingPostFlushCbs中放入工作时,都会执行queueFlush()办法,queueFlush办法会更新currentFlushPromise为最新的promise。所以应用nextTick传入的函数会在flushJobs之后执行。