当前位置: 首页 > news >正文

node 中的 nextTick 和 vue 中的 nextTick 的区别

node 中的 nextTick

node 中的 nextTick 是 node 自带全局的变量 process 的一个方法,process.nextTick 是一个微任务,在 node 的所有微任务中最先执行,是优先级最高的微任务。浏览器中是没有这一个方法的。

vue 中的 nextTick

vue 中的 nextTick 和 node 中的完全不同的东西,是 vue 源码中有自己的实现方法,而且 vue2 和 vue3 中的实现方法还不同。作用是在下次 DOM 更新循环结束之后执行延迟回调,

在下次 DOM 更新循环结束之后执行延迟回调

vue2 中的 nextTick 实现方法

在 vue2 源码中有一个专门的文件用来实现 nextTick 方法,可以自己去看一下,他依次判断并使用了 promise、mutationObserver、setImmediate、setTimeout 来调用回调函数

源代码如下

/* globals MutationObserver */import { noop } from 'shared/util'
import { handleError } from './error'
import { isIE, isIOS, isNative } from './env'export let isUsingMicroTask = falseconst callbacks: Array<Function> = []
let pending = falsefunction flushCallbacks() {pending = falseconst copies = callbacks.slice(0)callbacks.length = 0for (let i = 0; i < copies.length; i++) {copies[i]()}
}// Here we have async deferring wrappers using microtasks.
// In 2.5 we used (macro) tasks (in combination with microtasks).
// However, it has subtle problems when state is changed right before repaint
// (e.g. #6813, out-in transitions).
// Also, using (macro) tasks in event handler would cause some weird behaviors
// that cannot be circumvented (e.g. #7109, #7153, #7546, #7834, #8109).
// So we now use microtasks everywhere, again.
// A major drawback of this tradeoff is that there are some scenarios
// where microtasks have too high a priority and fire in between supposedly
// sequential events (e.g. #4521, #6690, which have workarounds)
// or even between bubbling of the same event (#6566).
let timerFunc// The nextTick behavior leverages the microtask queue, which can be accessed
// via either native Promise.then or MutationObserver.
// MutationObserver has wider support, however it is seriously bugged in
// UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It
// completely stops working after triggering a few times... so, if native
// Promise is available, we will use it:
/* istanbul ignore next, $flow-disable-line */
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.xMutationObserver.toString() === '[object MutationObserverConstructor]')
) {// Use MutationObserver where native Promise is not available,// e.g. PhantomJS, iOS7, Android 4.4// (#6466 MutationObserver is unreliable in IE11)let counter = 1const observer = new MutationObserver(flushCallbacks)const textNode = document.createTextNode(String(counter))observer.observe(textNode, {characterData: true})timerFunc = () => {counter = (counter + 1) % 2textNode.data = String(counter)}isUsingMicroTask = true
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {// Fallback to setImmediate.// Technically it leverages the (macro) task queue,// but it is still a better choice than setTimeout.timerFunc = () => {setImmediate(flushCallbacks)}
} else {// Fallback to setTimeout.timerFunc = () => {setTimeout(flushCallbacks, 0)}
}export function nextTick(): Promise<void>
export function nextTick<T>(this: T, cb: (this: T, ...args: any[]) => any): void
export function nextTick<T>(cb: (this: T, ...args: any[]) => any, ctx: T): void
/*** @internal*/
export function nextTick(cb?: (...args: any[]) => any, ctx?: object) {let _resolvecallbacks.push(() => {if (cb) {try {cb.call(ctx)} catch (e: any) {handleError(e, ctx, 'nextTick')}} else if (_resolve) {_resolve(ctx)}})if (!pending) {pending = truetimerFunc()}// $flow-disable-lineif (!cb && typeof Promise !== 'undefined') {return new Promise(resolve => {_resolve = resolve})}
}

vue3 中 nextTick 实现方法

vue3 中使用 promise 来实现,nextTick 方法返回一个 Promise 对象,因此可以使用 Promise 的链式调用或 async/await 语法来处理 nextTick 回调。在源码的scheduler.ts 文件中定义

完整代码如下

import { ErrorCodes, callWithErrorHandling, handleError } from './errorHandling'
import { type Awaited, NOOP, isArray } from '@vue/shared'
import { type ComponentInternalInstance, getComponentName } from './component'export interface SchedulerJob extends Function {id?: numberpre?: booleanactive?: booleancomputed?: 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/*** Attached by renderer.ts when setting up a component's render effect* Used to obtain component information when reporting max recursive updates.* dev only.*/ownerInstance?: ComponentInternalInstance
}export type SchedulerJobs = SchedulerJob | SchedulerJob[]let isFlushing = false
let isFlushPending = falseconst queue: SchedulerJob[] = []
let flushIndex = 0const pendingPostFlushCbs: SchedulerJob[] = []
let activePostFlushCbs: SchedulerJob[] | null = null
let postFlushIndex = 0const resolvedPromise = /*#__PURE__*/ Promise.resolve() as Promise<any>
let currentFlushPromise: Promise<void> | null = nullconst RECURSION_LIMIT = 100
type CountMap = Map<SchedulerJob, number>export function nextTick<T = void, R = void>(this: T,fn?: (this: T) => R,
): Promise<Awaited<R>> {const p = currentFlushPromise || resolvedPromisereturn fn ? p.then(this ? fn.bind(this) : fn) : p
}// #2768
// Use binary-search to find a suitable position in the queue,
// so that the queue maintains the increasing order of job's id,
// which can prevent the job from being skipped and also can avoid repeated patching.
function findInsertionIndex(id: number) {// the start index should be `flushIndex + 1`let start = flushIndex + 1let end = queue.lengthwhile (start < end) {const middle = (start + end) >>> 1const middleJob = queue[middle]const middleJobId = getId(middleJob)if (middleJobId < id || (middleJobId === id && middleJob.pre)) {start = middle + 1} else {end = middle}}return start
}export function queueJob(job: SchedulerJob) {// the dedupe search uses the startIndex argument of Array.includes()// by default the search index includes the current job that is being run// so it cannot recursively trigger itself again.// if the job is a watch() callback, the search will start with a +1 index to// allow it recursively trigger itself - it is the user's responsibility to// ensure it doesn't end up in an infinite loop.if (!queue.length ||!queue.includes(job,isFlushing && job.allowRecurse ? flushIndex + 1 : flushIndex,)) {if (job.id == null) {queue.push(job)} else {queue.splice(findInsertionIndex(job.id), 0, job)}queueFlush()}
}function queueFlush() {if (!isFlushing && !isFlushPending) {isFlushPending = truecurrentFlushPromise = resolvedPromise.then(flushJobs)}
}export function invalidateJob(job: SchedulerJob) {const i = queue.indexOf(job)if (i > flushIndex) {queue.splice(i, 1)}
}export function queuePostFlushCb(cb: SchedulerJobs) {if (!isArray(cb)) {if (!activePostFlushCbs ||!activePostFlushCbs.includes(cb,cb.allowRecurse ? postFlushIndex + 1 : postFlushIndex,)) {pendingPostFlushCbs.push(cb)}} else {// if cb is an array, it is a component lifecycle hook which can only be// triggered by a job, which is already deduped in the main queue, so// we can skip duplicate check here to improve perfpendingPostFlushCbs.push(...cb)}queueFlush()
}export function flushPreFlushCbs(instance?: ComponentInternalInstance,seen?: CountMap,// if currently flushing, skip the current job itselfi = isFlushing ? flushIndex + 1 : 0,
) {if (__DEV__) {seen = seen || new Map()}for (; i < queue.length; i++) {const cb = queue[i]if (cb && cb.pre) {if (instance && cb.id !== instance.uid) {continue}if (__DEV__ && checkRecursiveUpdates(seen!, cb)) {continue}queue.splice(i, 1)i--cb()}}
}export function flushPostFlushCbs(seen?: CountMap) {if (pendingPostFlushCbs.length) {const deduped = [...new Set(pendingPostFlushCbs)].sort((a, b) => getId(a) - getId(b),)pendingPostFlushCbs.length = 0// #1947 already has active queue, nested flushPostFlushCbs callif (activePostFlushCbs) {activePostFlushCbs.push(...deduped)return}activePostFlushCbs = dedupedif (__DEV__) {seen = seen || new Map()}for (postFlushIndex = 0;postFlushIndex < activePostFlushCbs.length;postFlushIndex++) {if (__DEV__ &&checkRecursiveUpdates(seen!, activePostFlushCbs[postFlushIndex])) {continue}activePostFlushCbs[postFlushIndex]()}activePostFlushCbs = nullpostFlushIndex = 0}
}const getId = (job: SchedulerJob): number =>job.id == null ? Infinity : job.idconst comparator = (a: SchedulerJob, b: SchedulerJob): number => {const diff = getId(a) - getId(b)if (diff === 0) {if (a.pre && !b.pre) return -1if (b.pre && !a.pre) return 1}return diff
}function flushJobs(seen?: CountMap) {isFlushPending = falseisFlushing = trueif (__DEV__) {seen = seen || new Map()}// Sort queue before flush.// This ensures that:// 1. Components are updated from parent to child. (because parent is always//    created before the child so its render effect will have smaller//    priority number)// 2. If a component is unmounted during a parent component's update,//    its update can be skipped.queue.sort(comparator)// conditional usage of checkRecursiveUpdate must be determined out of// try ... catch block since Rollup by default de-optimizes treeshaking// inside try-catch. This can leave all warning code unshaked. Although// they would get eventually shaken by a minifier like terser, some minifiers// would fail to do that (e.g. https://github.com/evanw/esbuild/issues/1610)const check = __DEV__? (job: SchedulerJob) => checkRecursiveUpdates(seen!, job): NOOPtry {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 = 0queue.length = 0flushPostFlushCbs(seen)isFlushing = falsecurrentFlushPromise = null// some postFlushCb queued jobs!// keep flushing until it drains.if (queue.length || pendingPostFlushCbs.length) {flushJobs(seen)}}
}function checkRecursiveUpdates(seen: CountMap, fn: SchedulerJob) {if (!seen.has(fn)) {seen.set(fn, 1)} else {const count = seen.get(fn)!if (count > RECURSION_LIMIT) {const instance = fn.ownerInstanceconst componentName = instance && getComponentName(instance.type)handleError(`Maximum recursive updates exceeded${componentName ? ` in component <${componentName}>` : ``}. ` +`This means you have a reactive effect that is mutating its own ` +`dependencies and thus recursively triggering itself. Possible sources ` +`include component template, render function, updated hook or ` +`watcher source function.`,null,ErrorCodes.APP_ERROR_HANDLER,)return true} else {seen.set(fn, count + 1)}}
}

相关文章:

  • 网络学习学习笔记
  • HDFSRPC安全认证Token篇2
  • 【MATLAB源码-第13期】基于matlab的4ASK的误码率BER和误符号率SER理论和实际对比仿真。
  • 外包干了17天,技术倒退明显
  • 用于3D建模的好文章
  • Nevion 3G-SDI系列光端机
  • 如何用Python编写简单的网络爬虫(页面代码简单分析过程)
  • 使用Docker部署开源项目FreeGPT35来免费调用ChatGPT3.5 API
  • C语言-翁恺-PTA-121-160课后练习题-04
  • RTSP/Onvif视频安防监控平台EasyNVR调用接口返回匿名用户名和密码的原因排查
  • 百度驾驶证C++离线SDK V1.1 C#接入
  • oracle全量、增量备份
  • 学习云计算HCIE选择誉天有什么优势?
  • 报修小程序怎么建立?维修服务行业的智能化升级
  • 【自控笔记】线性系统时域分析法
  • 网络传输文件的问题
  • 【跃迁之路】【699天】程序员高效学习方法论探索系列(实验阶段456-2019.1.19)...
  • 002-读书笔记-JavaScript高级程序设计 在HTML中使用JavaScript
  • 2018以太坊智能合约编程语言solidity的最佳IDEs
  • Angularjs之国际化
  • centos安装java运行环境jdk+tomcat
  • JavaScript的使用你知道几种?(上)
  • Java多态
  • JS学习笔记——闭包
  • mysql 数据库四种事务隔离级别
  • Python socket服务器端、客户端传送信息
  • Redis的resp协议
  • SQL 难点解决:记录的引用
  • 基于Javascript, Springboot的管理系统报表查询页面代码设计
  • 基于游标的分页接口实现
  • 普通函数和构造函数的区别
  • 前端知识点整理(待续)
  • 融云开发漫谈:你是否了解Go语言并发编程的第一要义?
  • # 手柄编程_北通阿修罗3动手评:一款兼具功能、操控性的电竞手柄
  • # 透过事物看本质的能力怎么培养?
  • $(selector).each()和$.each()的区别
  • (4)STL算法之比较
  • (c语言版)滑动窗口 给定一个字符串,只包含字母和数字,按要求找出字符串中的最长(连续)子串的长度
  • (附源码)springboot 基于HTML5的个人网页的网站设计与实现 毕业设计 031623
  • (附源码)ssm航空客运订票系统 毕业设计 141612
  • (五)大数据实战——使用模板虚拟机实现hadoop集群虚拟机克隆及网络相关配置
  • (新)网络工程师考点串讲与真题详解
  • (转贴)用VML开发工作流设计器 UCML.NET工作流管理系统
  • *_zh_CN.properties 国际化资源文件 struts 防乱码等
  • .NET CF命令行调试器MDbg入门(三) 进程控制
  • .NET8.0 AOT 经验分享 FreeSql/FreeRedis/FreeScheduler 均已通过测试
  • .NET的数据绑定
  • .Net下使用 Geb.Video.FFMPEG 操作视频文件
  • .pings勒索病毒的威胁:如何应对.pings勒索病毒的突袭?
  • .pyc文件还原.py文件_Python什么情况下会生成pyc文件?
  • @hook扩展分析
  • @modelattribute注解用postman测试怎么传参_接口测试之问题挖掘
  • [ 代码审计篇 ] 代码审计案例详解(一) SQL注入代码审计案例
  • [23] GaussianAvatars: Photorealistic Head Avatars with Rigged 3D Gaussians
  • [Android]如何调试Native memory crash issue