Vue nextTick 原理实现

发布于 2023-11-15 18:54:21 字数 3877 浏览 24 评论 0

Vue.nextTick 延迟一个任务使其异步执行,在下一个 tick 时执行。

现在来看看源码,nextTick 是一个立即执行函数,返回一个 function,这个函数的作用是在 task 或者 microtask 中推入一个 timerFunc,当前调用栈执行完以后以此执行直到执行到 timerFunc,目的是延迟到当前调用栈执行完以后执行

export const nextTick = (function() {
  // 存放异步执行的回调
  const callbacks = [];
  // 一个标志位,如果 timerFunc 已经被推送到任务队列中 则不会重复推送
  let pending = false;
  let timerFunc;
  // 下一个 tick 执行的回调,
  function nextTickHandler() {
    pending = false; // 恢复标志位 以便下次推送到任务队列
    const copies = callbacks.slice(0);
    callbacks.length = 0;
    /*执行所有 callback*/
    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 */
  // 这里解释一下,一共有 Promise、MutationObserver 以及 setTimeout 三种尝试得到 timerFunc 的方法
  // 优先使用 Promise,在 Promise 不存在的情况下使用 MutationObserver,这两个方法都会在 microtask 中执行,会比 setTimeout 更早执行,所以优先使用。
  // 如果上述两种方法都不支持的环境则会使用 setTimeout,在 task 尾部推入这个函数,等待调用执行。
if (typeof Promise !== 'undefined' && isNative(Promise)) {
  const p = Promise.resolve();
  timerFunc = () => {
    p.then(flushCallbacks);
    // In problematic UIWebViews, Promise.then doesn't completely break, but
    // it can get stuck in a weird state where callbacks are pushed into the
    // microtask queue but the queue isn't being flushed, until the browser
    // needs to do some other work, e.g. handle a timer. Therefore we can
    // "force" the microtask queue to be flushed by adding an empty timer.
    if (isIOS) setTimeout(noop);
  };
  isUsingMicroTask = true;
} else if (
  !isIE &&
  typeof MutationObserver !== 'undefined' &&
  (isNative(MutationObserver) ||
    // PhantomJS and iOS 7.x
    MutationObserver.toString() === '[object MutationObserverConstructor]')
) {
  // Use MutationObserver where native Promise is not available,
  // e.g. PhantomJS, iOS7, Android 4.4
  // (#6466 MutationObserver is unreliable in IE11)
  // 新建一个 textNode 的 DOM 对象,用 MutationObserver 绑定该 DOM 并指定回调函数,在 DOM 变化的时候则会触发回调,该回调会进入主线程(比任务队列优先执行),即 textNode.data = String(counter)时便会触发回调*/
  let counter = 1;
  const observer = new MutationObserver(flushCallbacks);
  const textNode = document.createTextNode(String(counter));
  observer.observe(textNode, {
    characterData: true,
  });
  timerFunc = () => {
    counter = (counter + 1) % 2;
    textNode.data = String(counter);
  };
  isUsingMicroTask = true;
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
  // Fallback to setImmediate.
  // Techinically it leverages the (macro) task queue,
  // but it is still a better choice than setTimeout.
  timerFunc = () => {
    setImmediate(flushCallbacks);
  };
} else {
  // Fallback to setTimeout.
  ///*使用 setTimeout 将回调推入任务队列尾部*/
  timerFunc = () => {
    setTimeout(flushCallbacks, 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) {
     pending = true;
     timerFunc();
    }
    // $flow-disable-line
    if (!cb && typeof Promise !== 'undefined') {
      return new Promise((resolve, reject) => {
        _resolve = resolve;
      });
    }
  };
})();

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

苍白女子

暂无简介

0 文章
0 评论
23 人气
更多

推荐作者

金兰素衣

文章 0 评论 0

ゃ人海孤独症

文章 0 评论 0

一枫情书

文章 0 评论 0

清晰传感

文章 0 评论 0

mb_XvqQsWhl

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文