vue源码学习总结 深入解析nextTick

nextTick实现机制

优先级:setImmediate() > MessageChannel > Promise.resolve().then() > setTimeout(fn,0)

执行过程

当前tick =>
当前tick第一次调用queueNextTick 的时候将回调放入callbacks中,且将timerFunc推入到宏/微任务栈中,设置pending锁=>
当前tick后续调用queueNextTick 的时候将回调放入callbacks中 =>
当前tick执行完毕后,执行timerFunc,执行所有callbacks,解除pending 锁=>
下一tick循环

源码实现

// src/core/util/env.js

/**
 * Defer a task to execute it asynchronously.
 */
export const nextTick = (function () {
  const callbacks = []
  let pending = false
  let timerFunc

  // 依次执行所有回调函数 callbacks
  function nextTickHandler () {
    pending = false
    const copies = callbacks.slice(0)
    callbacks.length = 0
    for (let i = 0; i < copies.length; i++) {
      copies[i]()
    }
  }

  // An asynchronous deferring mechanism.
  // In pre 2.4, we used to use microtasks (Promise/MutationObserver)
  // but microtasks actually has too high a priority and fires in between
  // supposedly sequential events (e.g. #4521, #6690) or even between
  // bubbling of the same event (#6566). Technically setImmediate should be
  // the ideal choice, but it's not available everywhere; and the only polyfill
  // that consistently queues the callback after all DOM events triggered in the
  // same loop is by using MessageChannel.
  /* istanbul ignore if */
  // nextTick实现机制: setImmediate() > MessageChannel > Promise.resolve().then() > setTimeout(fn,0)
  if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
    timerFunc = () => {
      setImmediate(nextTickHandler)
    }
  } else if (typeof MessageChannel !== 'undefined' && (
    isNative(MessageChannel) ||
    // PhantomJS
    MessageChannel.toString() === '[object MessageChannelConstructor]'
  )) {
    const channel = new MessageChannel()
    const port = channel.port2
    channel.port1.onmessage = nextTickHandler
    timerFunc = () => {
      port.postMessage(1)
    }
  } else
  /* istanbul ignore next */
  if (typeof Promise !== 'undefined' && isNative(Promise)) {
    // use microtask in non-DOM environments, e.g. Weex
    const p = Promise.resolve()
    timerFunc = () => {
      p.then(nextTickHandler)
    }
  } else {
    // fallback to setTimeout
    timerFunc = () => {
      setTimeout(nextTickHandler, 0)
    }
  }

  return function queueNextTick (cb?: Function, ctx?: Object) {
    let _resolve
    callbacks.push(() => {
      if (cb) {
        try {
          cb.call(ctx)
        } catch (e) {
          handleError(e, ctx, 'nextTick')
        }
      } else if (_resolve) {
        _resolve(ctx)
      }
    })
    if (!pending) {
      // 第一次有回调函数进来的时候,将nextTick推入宏/微任务栈中,本次tick后续再有回调函数进来的时候,不再重复将nextTick推入宏/微任务栈中,直到nextTick被执行为止。
      pending = true
      timerFunc()
    }
    // $flow-disable-line
    if (!cb && typeof Promise !== 'undefined') {
      return new Promise((resolve, reject) => {
        _resolve = resolve
      })
    }
  }
})()

关注下再走呗 +_+

发布了45 篇原创文章 · 获赞 86 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/lyt_angularjs/article/details/105280628