事件循环

    0

⚠️本篇只讨论浏览器部分

概念

进程(process)和线程(thread)

进程(process):计算机已经运行的程序,是操作系统管理程序的一种方式。 线程(thread):操作系统能够运行运算调度的最小单位,通常情况下它被包含在进程中。

浏览器是多进程的,通常每个tab页都是一个进程,一个页面崩溃不会影响其他页面

  1. 进程是操作系统分配资源的最小单位,线程是程序执行的最小单位。
  2. 一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线;
  3. 进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间(包括代码段、数据集、堆等)及一些进程级的资源(如打开文件和信号)。
  4. 调度和切换:线程上下文切换比进程上下文切换要快得多。

以谷歌浏览器为例,由以下线程组成

  • GUI 渲染线程

  • JavaScript引擎线程

  • 定时触发器线程

  • 事件触发线程

  • 异步http请求线程

GUI渲染线程与JavaScript引擎互斥、所以JS会阻塞页面的加载

调用栈

管理执行上下文的栈为调用栈、先进后出,当入栈的执行上下文超过了一定数目,JS引擎就会报栈溢出错误

微任务和宏任务

JavaScript执行分为同步任务和异步任务,同步任务压入执行栈中执行,异步任务放入任务队列中,任务队列又分为微任务队列和宏任务队列

宏任务有哪些

  • script标签中运行代码
  • 事件的回调函数
  • setTimeout,setInterval,setImmediate, I/O, UI rendering,requestAnimationFrame

微任务有哪些

  • Promise
  • MutationObserver
  • process.nextTick, Promises, Object.observe, MutationObserver,fetch

任务队列

先进先出的数据结构

执行机制

简单概括

  1. 执行栈中的同步任务,如果存在微任务,则该任务进入微任务队列中;如果存在宏任务,则该任务进入宏任务队列中
  2. 在执行栈清空后,检查微任务队列中的微任务,微任务队列里的微任务依次出列执行(先进先出),直至队列清空;
  3. 检查宏任务队列中的宏任务,宏任务队列取出一个任务执行,执行步骤1

⚠️在所有任务开始的时候,由于宏任务中包括了script,所以浏览器会先执行一个宏任务,在这个过程中你看到的延迟任务(例如setTimeout)将被放到下一轮宏任务中来执行。

浏览器渲染事件循环

  1. 一开始整个脚本作为一个宏任务执行
  2. 执行过程中同步代码直接执行,宏任务进入宏任务队列,微任务进入微任务队列
  3. 当前宏任务执行完出队,检查微任务列表,有则依次执行,直到全部执行完
  4. 执行浏览器UI线程的渲染工作
  5. 检查是否有Web Worker任务,有则执行
  6. 执行完本轮的宏任务,回到2,依此循环,直到宏任务和微任务队列都为空

什么是Event Loop

  • JavaScript是单线程的语言

  • 事件循环是JavaScript的执行机制

  • JavaScript事件分同步和异步事件

JavaScript引擎在等待任务,执行任务中不断循环,宏任务和微任务执行完成后都会判断是否还有微任务,有的话执行微任务,没有就执行宏任务,如此循环

用代码表示即

for (const macroTask of macroTaskQueue) {  

  handleMacroTask();    

  for (const microTask of microTaskQueue) {    

      handleMicroTask(microTask);  

  }

}

题目1

js
console.log(0) setTimeout(_ => console.log(4),0) new Promise(resolve => { resolve() console.log(1) }).then(_ => { console.log(3) }) console.log(2)

题目2

js
async function fn1() { console.log(1) } async function fn2() { console.log(2) } async function result() { await fn1() fn2() setTimeout(() => { console.log(3); }); Promise.resolve().then(res => console.log(4)) console.log(5); } result() console.log(6) Promise.resolve().then(res => console.log(7)) setTimeout(() => { console.log(8); });

node

Node的事件循环是基于libuv实现的

待更新

总结

  1. js工作机制是单线程的,但是任务有同步任务和异步任务之分
  2. 执行任务的地方叫执行栈,而异步任务会进入任务队列等待被执行
  3. 执行栈中的任务执行完毕之后,执行栈会去查看任务队列中还有没有未执行的任务。有,则拿出来执行;没有,则开始下一次的工作
  4. 重复第三点的工作,不断地处理后续的任务,这样的工作机制就是事件循环

F&Q

  1. 为啥是单进程

如果 JavaScript 引擎线程不是单线程的,那么可以同时执行多段 JavaScript,如果这多段 JavaScript 都修改 DOM,那么就会出现 DOM 冲突。所以避免 DOM 渲染的冲突

  1. 为什么有异步

引入单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。这同时又导致了一个问题:如果前一个任务耗时很长,后一个任务就不得不一直等着。

评论区

共有评论 0

暂无评论