基本
js执行主线程不断从任务队列中提取宏任务执行,并在执行后执行清空所有微任务。
一个event loop执行的过程
一个event loop只要存在,就会不断执行下边的步骤:
1.在tasks队列中选择最老的一个task,用户代理可以选择任何task队列,如果没有可选的任务,则跳到下边的microtasks步骤。
2.将上边选择的task设置为正在运行的task。
3.Run: 运行被选择的task。
4.将event loop的currently running task变为null。
5.从task队列里移除前边运行的task。
6.Microtasks: 执行microtasks任务检查点。(也就是执行microtasks队列里的任务)
7.检查requsetAnimationFram如果有则执行->更新渲染(Update the rendering),
8.如果是一个worker event loop,没有任务在队列中且closing标识为true,则销毁event loop。
9.返回到第一步。
一些点
- 队列可以有一个或多个,不同来源的队列可能会被推入不同的队列
- 队列是set而非queues,拿去任务是拿去最老的任务而非出队列事件
- 微任务结束后进行视图的更新渲染。
- 定时任务/异步请求任务本质也是同步任务,执行时通知定时器触发线程或者网络请求加入到异步堆,事件触发条件满足后再把回调任务加入任务队列。把这个过程看作黑盒后对于回调任务来说是异步的。
- 宏任务一:整体代码script,setTimeout,setInterval、setImmediate。微任务:原生Promise(有些实现的promise将then方法放到了宏任务中)、process.nextTick(node)、Object.observe(已废弃)、 MutationObserver。
requestAnimationFrame可以在下次渲染之前执行回调函数一般用与动画。执行频率与屏幕刷新率一般同步。- MutationObserver监控一个空节点,通过修改它的值来触发回调执行微任务。
- 更新渲染的执行不是一定的,就像requsetAnimationFram规定的一样自带节流,最短执行一次与显示器刷新率同步。
执行栈/执行上下文
调取的任务被推入执行栈中,栈中底部是这个任务需要的局部变量等上下文,在任务结束后这些上下文不用的会弹出。在一个script块结束这些上下文内容弹出也是为什么定义的变量作用域的来源。
在js线程执行这个栈顶任务之前,会先做这两事情:向下寻找this的指向/压入变量环境与词法环境。在执行结束后压入的环境内容弹出。
(变量环境与此法环境的不同是变量环境专门储存var,等它废弃的那一天应该就没了)
(闭包调用生成新的实例将即将出栈的内容保存在闭包实例中)