JavaScript Event Loop

单线程模型

单线程模型指的是,JavaScript只能在一个线程上运行,也就是说只能同时指向一个任务,其他任务都必须在后面排队等待。注意:虽然JavaScript只在一个线程上运行,但并不代码JavaScript引擎只有一个线程。事实上,JavaScript引擎有多个线程,单个脚本只能在一个线程上运行(主线程),其他线程都是在后台配合。

JavaScript Event Loop_第1张图片

  • JS引擎线程:也叫JS内核,负责解析执行JS脚本程序的主程序,例如Charome的V8引擎
  • 事件触发线程:属于浏览器内核线程,主要用于控制事件,例如鼠标、键盘等,当事件被触发后,就会把事件的处理函数推进事件队列,等待JS引擎执行
  • 定时器触发线程:主要控制setTimeoutsetInterval,用来计时,计时完毕后,则把定时器处理的函数推进事件队列,等待JS引擎执行
  • HTTP异步请求线程:通过XMLHttpRequest连接后,通过浏览器新开的一个线程,监控readyState状态变更时,如果设置了该状态的回调函数,则将该状态的处理函数推进事件队列中,等待JS引擎执行

疑问?

  • JavaScript为什么要采用单线程,而不是多线程?
    不想让浏览器变得复杂(避免复杂性),同时避免 DOM 渲染冲突,因为多线程需要共享资源、且可能修改彼此运行的结果

  • 该模式会导致的问题?
    如果单个任务耗时长,会拖延整个程序的执行,可能导致浏览器无响应(假死)

  • JavaScript是如何解决这个问题的?
    因为单线程的原因,CPU很多时候都闲着的,并且因为IO操作(输入输出)很慢(比如Ajax操作从网络读取数据),这时CPU可以完全不管IO操作,挂起等待中的任务,先运行排在后面的任务。等到IO操作返回了结果,再回过头,把挂起的任务继续执行下去。这种机制就是JavaScript内部采用的“事件循环”机制(Event Loop)

宏任务

可以分成两类:同步任务(synchronous)和异步任务(asynchronous)

  • 同步任务:没有被引擎挂起、在主线程上排队执行的任务。只有前一个任务执行完毕,才能执行后一个任务。

  • 异步任务:被引擎放一边、不进行主线程、而进入任务队列的任务。只有引擎认为某个异步任务可以执行了(比如Ajax操作从服务器得到了结果),那么该任务(通过回调函数的形式)才能进入主线程执行。排在异步任务后面的代码,不用等到异步任务结束就会马上运行,也就是说,异步任务不具有“堵塞”效应

微任务

微任务是ES6的Promise和Node环境下的process.nextTick(笔主对Node未深入学习,所以这里只是简单说明有这个东西)

注意

微任务的执行在宏任务的同步任务之后,在异步任务之前

JavaScript Event Loop_第2张图片

代码例子

console.log(1) // 宏任务 同比

setTimeout(() => {
  console.log(2) // 宏任务 异步
}, 0)

new Promise((resolve) => {
  console.log(3) // 宏任务 同步
  resolve()
}).then(() => {
  console.log(4) // 微任务 (Promise实例对象返回的才是微任务!!)
})

console.log(5) // 宏任务 同步

以上代码输出为:1 3 5 4 2

任务队列和事件循环

JavaScript运行时,除了一个正在运行的主线程(又称为“调用栈(call stack)”),引擎还提供了一个任务队列(task queue),里面是各种需要处理当前程序处理的异步任务。(实际上,根据异步任务的类型,存在多个任务队列)

JavaScript Event Loop_第3张图片

1.主线程会去执行所有的宏任务中的同步任务。
2.等到宏任务中的同步任务全部执行完,查看任务队列中的异步任务和微任务队列,将满足条件的按照先微任务再异步的准则重新进入主线程开始执行,这时候就变成同步任务。
3.等任务执行完,下一个任务再进入主线程开始执行。
4.引擎不停检查(“事件循环”),一旦任务队列清空,程序就结束执行。

参考文档

2分钟了解 JavaScript Event Loop | 面试必备
在浏览器输入 URL 回车之后发生了什么(超详细版)

你可能感兴趣的:(JavaScript Event Loop)