事件循环(event loop) && 任务队列 (task queue) && $nextTick
Js的异步机制是由事件循环和任务队列构成, Js本身是单线程的语言,所谓异步依赖于浏览器或者操作系统等完成。 javascript主线程拥有一个执行栈以及一个队伍队列,主线程会一次执行代码,当遇到函数时,会先将函数入栈,函数运行完毕后再将函数出栈,直到所有代码执行完毕。
遇到异步操作时,异步操作会由浏览器(os)执行,浏览器会在这些任务完成后,将事先定义的回调函数推入主线程的任务队列(task queue)中,当主线程的执行栈清空之后读取task queue中的回调函数,当task queue 读取完毕之后,主线程接着执行,从而进入一个无限的循环,这就是事件循环。
一个浏览器环境只能有一个事件循环,而一个事件循环可以多个任务队列,每个任务都有一个任务源(task source);但是,同一个任务队列中的任务必须按照先进先出的顺序执行。多个人物队列是为了方便控制优先级。任务队列是一个先进先出的队列。
macrotsk和microtask是异步任务的两种分类。 在挂起任务时,js引擎会将所有任务按照类分到这两个队列中,首先在macrotask 的队列(这个队列也被叫做task queue)中取出第一个任务,执行完毕后取出microtask队列中的所有任务顺序执行;之后再取出macrotask任务,周而复始,直至两个队列的任务都取完。
所有的代码(script)是一个macrotask,js先执行一个macrotask,执行过程中遇到异步操作则创建一个macrotask,遇到process.nectTick,promise等创建一个microtask,这两个queue分别被挂起。执行栈为空时,开始处理macrotask,完成后处理microtask,直到该microtask全部执行完,然后继续主线程调用栈。 每一次事件循环只处理一个macrotask,完成后处理microtask,知道该microtask全部执行完,然后继续主线程调用栈。 处理这些microtask是还可以将跟多的microtask入队,他们会在一一执行,直到整个microtask队列处理完。
简而言之,异步的运行机制就是:
(1)所有同步任务都在主线上执行,形成一个执行栈;
(2)主线程之外还存在一个任务队列,只要异步任务有了运行结果,就在任务队列之中存放一个事件;
(3)一旦执行栈中的所有同步任务执行完成,系统就会读取任务队列,看看里边有哪些事件;那些对应的异步任务,就结束等待,进入执行栈,开始执行。
(4)主线程不断的重复执行第三步,直至任务队列没有任务
vue中的$nextTick是在下次Dom更新循环结束之后执行延迟回调,在修改数据之后使用$nextTick,则可以在回调中获取更新后的dom;
Event Loop
程序运行以后被叫做进程(process),一般情况下,一个进程一次只能执行一个任务;如果要执行多个任务,那么只能:(1):排队;(2):新建进程;(3): 新建线程;
我们知道javascript是一种单线程的语言,所有的任务都在一个线程上完成,即采用排队的方法。一旦遇到大量的任务或者遇到一个耗时的任务,网页就会出现"假死",为javascrip停不下来,也无法响应
用户的行为。至于javascript为什么是单线程,因为这是一种约定,多线程需要共享资源、且有可能是修改彼此的运行结果,对于一种网页脚本语言来说,这个就太复杂了,因此为了不让浏览器变得太复杂,
就约定成单线程的了;(Worker API 可以实现多线程,但是JavaScript本身始终是单线程的。)
Event Loop是一个程序结构,用于等待和发送消息和事件。简单的来讲就是,在程序中设置两个线程:一个负责程序本身的运行,称为"主线程";另一个负责主线程与其他进程(主要是各种I/O操作)的通
信,被称为"Event Loop线程"; 每当遇到I/O程序完成操作,主线程就让Event Loop线程去通知相应的I/O程序,然后接着往后运行,所以不存在等待的时间。等到I/O程序完成操作, Event Loop线程再把结
果返回主线程。主线程就调用事先设定的回调函数,完成整个任务。 这种运行方式成为"异步模式"或"非阻塞模式"
欲查看$nextTick源码,请点击此处