We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
nextTick(flushSchedulerQueue)
nextTick是Vue里面一个比较核心的概念;不过在讲nextTick之前就必须要讲到JavaScript的运行机制和任务队列。
众所周知,浏览器的脚本语言是JavaScript,这个语言最大的特点就是单线程,也就是在同一时间只能干一件事情。
为什么是单线程的呢?假定有两个线程,一个操作dom,一个删除dom,岂不就乱套了~
当然为了充分利用CPU,Html5提出了web worker,允许开发人员创建多个线程,但是子线程完全受主线程控制,但是不得操作DOM,这也是遵循了单线程的标准。
单线程呢,也就意味着所有的任务,都需要排队运行,一个任务运行结束后,才会去执行下一个任务;
熟悉JavaScript的开发人员都明白,有异步回调这个概念,也就是说会挂起等待中的任务,去执行下一个任务,等回调回来再去执行被挂起的任务。
综上所述,任务分为两种,一个是同步任务(synchronous,简称sync),一个是异步任务(asynchronous,简称async)。
主进程会不断重复获取步骤,执行完一个qtask,则继续询问qtask任务队列,获取qtask,放到主线程来执行。
只要主线程空了,就会去读取"任务队列",这就是JavaScript的运行机制
任务队列,也就是异步任务的队列。分为两种类型的任务:微任务(microtask)和宏任务(macrotask)
宏任务(macrotask):
微任务(microtask):
console.log('main start'); setTimeout(() => { console.log('macrotask'); Promise.resolve().then(() => { console.log('microtask 1'); }) }, 0); Promise.resolve().then(() => { console.log('microtask 2'); Promise.resolve().then(() => { console.log('microtask 3'); }) }) console.log('main end');
上面模仿了一下微任务(Promise)和宏任务(setTimeout);微任务里面套了个微任务;宏任务里面套了个微任务; 输出如下:
main start main end microtask 2 microtask 3 macrotask microtask 1
可以分析下上面代码的执行顺序:
综上分析任务队列完成
经过上面的过程,相信大家都对浏览器的运行机制和任务队列有了足够的了解,也明白了任务队列中任务的执行顺序,接下来咱们看下nextTick的实现。文件位于/src/core/util/next-tick.js,先看下Vue里面任务的代码:
let timerFunc if (typeof Promise !== 'undefined' && isNative(Promise)) { const p = Promise.resolve() timerFunc = () => { p.then(flushCallbacks) if (isIOS) setTimeout(noop) } isUsingMicroTask = true } else if (!isIE && typeof MutationObserver !== 'undefined' && ( isNative(MutationObserver) || MutationObserver.toString() === '[object MutationObserverConstructor]' )) { let counter = 1 const observer = new MutationObserver(flushCallbacks) const textNode = document.createTextNode(String(counter)) observer.observe(textNode, { characterData: true }) timerFunc = () => { counter = (counter + 1) % 2 console.log('counter', counter) textNode.data = String(counter) } isUsingMicroTask = true } else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { timerFunc = () => { setImmediate(flushCallbacks) } } else { timerFunc = () => { setTimeout(flushCallbacks, 0) } }
上面代码是Vue对timerFun的定义,Vue倾向于微任务,毕竟微任务优先级是最高的,咱们来看下实现:
来看下nextTick的代码吧:
function nextTick (cb?: Function, ctx?: Object) { let _resolve callbacks.push(() => { if (cb) { try { cb.call(ctx) } catch (e) { handleError(e, ctx, 'nextTick') } } else if (_resolve) { console.log('_resolve') _resolve(ctx) } }) if (!pending) { pending = true timerFunc() } if (!cb && typeof Promise !== 'undefined') { return new Promise(resolve => { _resolve = resolve }) } }
上面部分代码,会对传进来的cb进行存储,放到全局变量callbacks里面,然后判断当前执行的状态,是否属于pending(类似Promise的pending状态)状态,可以理解为忙着呢,如果不忙,就让它忙起来,执行上面部分讲到的timerFun;这部分也就是咱们经常用到的
$nextTick(function() { dosomething.... })
下面咱们来看下调用timerFun后,执行的flushCallbacks;
function flushCallbacks () { pending = false const copies = callbacks.slice(0) callbacks.length = 0 for (let i = 0; i < copies.length; i++) { copies[i]() } }
执行开始,置为不忙状态,因为浏览器是单线程的,执行这段代码的时候,就不会执行别的代码,不用担心这时候会有别的事情影响此处代码的执行,也不用担心此时会有nextTick的调用,也就不用担心pending状态的此处改变会不会影响nextTick部分的逻辑;
此处代码很简单,获取回调的拷贝,然后把回调栈清空;依次执行回调。
#记得star
The text was updated successfully, but these errors were encountered:
No branches or pull requests
前篇
nextTick是Vue里面一个比较核心的概念;不过在讲nextTick之前就必须要讲到JavaScript的运行机制和任务队列。
JavaScript的运行机制
众所周知,浏览器的脚本语言是JavaScript,这个语言最大的特点就是单线程,也就是在同一时间只能干一件事情。
为什么是单线程的呢?假定有两个线程,一个操作dom,一个删除dom,岂不就乱套了~
当然为了充分利用CPU,Html5提出了web worker,允许开发人员创建多个线程,但是子线程完全受主线程控制,但是不得操作DOM,这也是遵循了单线程的标准。
单线程呢,也就意味着所有的任务,都需要排队运行,一个任务运行结束后,才会去执行下一个任务;
熟悉JavaScript的开发人员都明白,有异步回调这个概念,也就是说会挂起等待中的任务,去执行下一个任务,等回调回来再去执行被挂起的任务。
综上所述,任务分为两种,一个是同步任务(synchronous,简称sync),一个是异步任务(asynchronous,简称async)。
所以简要图示一下,就是这样的:
主进程会不断重复获取步骤,执行完一个qtask,则继续询问qtask任务队列,获取qtask,放到主线程来执行。
只要主线程空了,就会去读取"任务队列",这就是JavaScript的运行机制
任务队列
任务队列,也就是异步任务的队列。分为两种类型的任务:微任务(microtask)和宏任务(macrotask)
宏任务(macrotask):
微任务(microtask):
举个例子:
上面模仿了一下微任务(Promise)和宏任务(setTimeout);微任务里面套了个微任务;宏任务里面套了个微任务;
输出如下:
可以分析下上面代码的执行顺序:
综上分析任务队列完成
nextTick
经过上面的过程,相信大家都对浏览器的运行机制和任务队列有了足够的了解,也明白了任务队列中任务的执行顺序,接下来咱们看下nextTick的实现。文件位于/src/core/util/next-tick.js,先看下Vue里面任务的代码:
上面代码是Vue对timerFun的定义,Vue倾向于微任务,毕竟微任务优先级是最高的,咱们来看下实现:
来看下nextTick的代码吧:
上面部分代码,会对传进来的cb进行存储,放到全局变量callbacks里面,然后判断当前执行的状态,是否属于pending(类似Promise的pending状态)状态,可以理解为忙着呢,如果不忙,就让它忙起来,执行上面部分讲到的timerFun;这部分也就是咱们经常用到的
下面咱们来看下调用timerFun后,执行的flushCallbacks;
执行开始,置为不忙状态,因为浏览器是单线程的,执行这段代码的时候,就不会执行别的代码,不用担心这时候会有别的事情影响此处代码的执行,也不用担心此时会有nextTick的调用,也就不用担心pending状态的此处改变会不会影响nextTick部分的逻辑;
此处代码很简单,获取回调的拷贝,然后把回调栈清空;依次执行回调。
#记得star
The text was updated successfully, but these errors were encountered: