再谈事件循环

120 阅读4分钟

再谈事件循环

为什么再谈事件循环

  • 有一天,我突然想了一个问题,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线程】

image.png

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);
  1. 浏览器加载页面,浏览器加载页面,除了开辟堆栈内存,还创建了两个队列
  • webApi 任务监听队列
  • EventQueue 事件/任务队列
  1. 当主线程自上而下执行,如果遇到异步代码,会把异步任务放到webApi中去监听,
  • 浏览器会开辟新的线程去监听是否可以执行
  • 不会阻塞主线程的渲染和解析,会继续向下执行同步代码
  1. 当异步代码监测可以执行了,但是也不会立即执行,而是会挪到EventQueue中,去排队
  • 根据微任务和宏任务,放在不同的队列中
  • 谁先进来排队的,谁在各自队伍的最前面
  • 所以对于定时器来讲,设置了时间不会按照规定的时间执行
  1. 同步代码(同宏任务)全部执行完毕,主线程空闲下来,此时回去EventQueue中,把正在排队的异步任务,按顺序取出来执行
  2. 上述就是事件循环

所以js是如何实现异步的? 光靠单线程是不能实现异步的,浏览器是多线程的,当浏览器加载页面时,除了会开辟堆栈内存,还会创建两个队列 浏览器会给js分配一个线程,当执行栈执行主线程的代码时,遇到异步代码,会暂时将其挂起。浏览器会开辟新的线程,去做异步的监听,监听完毕之后,也不会立即执行,而是放到EventQueue中排队,等到主线程中代码执行完毕,执行栈为空,就会去EventQueue中按顺序取排队的任务,以上就是事件循环。所以js单线程的异步是通过浏览器的多线程和事件循环去实现的异步,简单来说,就是异步任务,会起来,浏览器的webApi会做异步任务的监听,监听完毕,放到EventQueue中排队,等主线程空闲,再按顺序,将异步出来

宏任务和微任务

image.png 事件循环机制

image.png