再谈事件循环
为什么再谈事件循环
- 有一天,我突然想了一个问题,js是单线程的,单线程还能做异步调用?,因为有事件循环?事件循环就能让单线程的js做异步了? 我觉得这足以说明单线程结合事件循环,就能做异步调用,所以开始了下面的学习
js 为什么是单线程,什么是单线程
摘自阮一峰大佬描述
JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。
为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。
说到这里,对于javascript 是单行程,就不会好奇了,既然是单行程,那么代码会一行一行执行,也就是同步代码,不会出现跳跃式执行,
谁赋予了javascript的单线程
javascript 作为浏览器脚本语言,自然是浏览器给它分配线程,浏览器只分配一个线程去解析js,所以js 是单线程的
浏览器还有哪些线程
- GUI渲染线程: 渲染和解析页面
- js引擎线程: 渲染和解析js(浏览器只分配一个线程去解析js,所以js 是单线程的)
- 定时器监听线程
- 事件监听线程
- HTTP网络请求线程【同源下,浏览器最多分配5~7个HTTP线程】
js是如何实现异步的?
看一段代码
setTimeout(() => {
console.log(1);
});
console.log(2);
for (let index = 0; index < 1000; index++) {
// dosomething
}
console.log(3);
setTimeout(() => {
console.log(4);
});
console.log(5);
- 浏览器加载页面,浏览器加载页面,除了开辟堆栈内存,还创建了两个队列
- webApi 任务监听队列
- EventQueue 事件/任务队列
- 当主线程自上而下执行,如果遇到异步代码,会把异步任务放到webApi中去监听,
- 浏览器会开辟新的线程去监听是否可以执行
- 不会阻塞主线程的渲染和解析,会继续向下执行同步代码
- 当异步代码监测可以执行了,但是也不会立即执行,而是会挪到EventQueue中,去排队
- 根据微任务和宏任务,放在不同的队列中
- 谁先进来排队的,谁在各自队伍的最前面
- 所以对于定时器来讲,设置了时间不会按照规定的时间执行
- 同步代码(同宏任务)全部执行完毕,主线程空闲下来,此时回去EventQueue中,把正在排队的异步任务,按顺序取出来执行
- 上述就是事件循环
所以js是如何实现异步的?
光靠单线程是不能实现异步的,浏览器是多线程的,当浏览器加载页面时,除了会开辟堆栈内存,还会创建两个队列 浏览器会给js分配一个线程,当执行栈执行主线程的代码时,遇到异步代码,会暂时将其挂起。浏览器会开辟新的线程,去做异步的监听,监听完毕之后,也不会立即执行,而是放到EventQueue中排队,等到主线程中代码执行完毕,执行栈为空,就会去EventQueue中按顺序取排队的任务,以上就是事件循环。所以js单线程的异步是通过浏览器的多线程和事件循环去实现的异步,简单来说,就是异步任务,会存起来,浏览器的webApi会做异步任务的监听,监听完毕,放到EventQueue中排队,等主线程空闲,再按顺序,将异步取出来
宏任务和微任务
事件循环机制