关于前端:vue3源码六scheduler

【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 = null
let 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 = false

function 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 = null
let preFlushIndex = 0

export 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之后执行。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理