javascript Event Loop·事件循环机制

 
基础知识(相关概念)
 
  • 进程:是系统资源分配和调度的单元。一个运行着的程序就对应了一个进程。一个进程包括了运行中的程序和程序所使用到的内存和系统资源。
  • 线程:线程是进程下的执行者,一个进程至少会开启一个线程(主线程),也可以开启多个线程。
  • 同步:发出调用后,在没有得到结果前,该调用不返回。但是一旦调用返回,就得到返回值
  • 异步:发出调用后,调用直接返回,没有返回结果。但结果由回调函数给出,至于什么时候给出,不知道。(这个回调函数肯定是在当前js执行完后才执行)
  • 阻塞调用:调用结果返回之前,当前线程被挂起。调用线程只有在得到结果后才会返回。
  • 非阻塞调用:在不能立刻得到结果之前,该调用不会阻塞当前线程。
 
JavaScript的线程和运行
   
    众所周知Js是单线程来运行的,那为什么会出现异步这种操作呢?
 
    首先我们需要弄清楚两个词:执行和运行
 
           执行和运行有很大的区别,javascript在不同的环境下,比如node,浏览器,Ringo等等,执行方式是不同的。而运行大多指   javascript解析引擎,是统一的。
 
     也就是说,虽然Js是单线程,但是执行环境(浏览器、node)是多线程的,浏览器可以协助Js引擎模拟出异步的效果。浏览器有以下常驻线程:
 
      渲染引擎线程:负责页面的渲染
      JS引擎线程:负责JS的解析和执行(本文说的主线程就指js引擎线程)
      定时器触发线程:处理定时事件,比如setTimeout, setInterval
      事件触发线程:处理DOM事件
      异步http请求线程:处理http请求 
        ......
 
    浏览器是Js的使用场景,浏览器本身是典型的 GUI 工作线程(GUI 工作线程在绝大多数系统中都实现为事件处理,避免阻塞交互)。故浏览器是事件驱动的(Event driven),浏览器中很多行为是异步,会创建事件并放入任务队列中。由于Javascript 是单线程,它需要借助异步完成耗时或者时间不确定的操作,这些操作由浏览器的其它线程执行,这形成了异步事件驱动。异步事件驱动往往由浏览器的两个或以上常驻线程共同完成的。例如ajax异步请求是由JS执行线程和异步http请求线程,事件触发线程共同完成的。
 
事件循环是:主线程重复从任务队列中取消息(事件),执行对应回调函数的过程。
 
 
通俗来说就是当JS引擎运行代码时,遇见同步代码则直接运行并返回结果,遇见异步代码就通知浏览器让对应的线程来接管异步事件,比如:异步http请求线程会来接管ajax相关的,事件触发线程会接管由点击事件或者一些dome事件所触发的方法等等。当相应的线程处理有结果时则把回调函数加入到任务队列(宏任务)中,事件触发线程是在事件被触发时才会把事件的回调函数加入到任务队列中。
 
任务队列与“事件列表”的详解
 
  • 事件列表指的是所有接管异步事件的线程们的集合抽象出来的,我们称之为 Event table.
  • 任务队列是由Event table 内已完成的异步事件的回调方法,比如:ajax请求成功以后的回调函数或者是定时器到时间以后的回调函数等,这些称之为:Event Queue。
  • ES6 规范中,microtask 称为 jobs,macrotask 称为 task
  • 宏任务是由宿主(浏览器、node)发起的,而微任务由JavaScript自身发起
  • 任务队列中宏任务是一个一个进入的,而微任务是按队列运行的,每次清空队列才算结束,所以微任务中还存在微任务的话是一次循环中执行的
  • 执行栈是通过先进后出运行代码的,任务队列是先进先出运行的
 
 
 
当某个宏任务执行完后,会查看是否有微任务队列。如果有,先执行微任务队列中的所有任务,如果没有,会读取宏任务队列中排在最前的任务,执行宏任务的过程中,遇到微任务,依次加入微任务队列。栈空后,再次读取微任务队列里的任务,依次类推。
 
在ES3以及以前的版本中,JavaScript本身没有发起异步请求的能力,也就没有微任务的存在。在ES5之后,JavaScript引入了Promise,这样,不需要浏览器,JavaScript引擎自身也能够发起异步任务了。
 
 
拓展 :async和await是如何处理异步任务的?
简单说,async是通过Promise包装异步任务。
比如有如下代码:
async function async1() {
    await async2() 
    console.log('async1 end') 
async function async2() {
    console.log('async2 end') 
console.log('async')
async1()
console.log('async center')
 
编译为ES5的写法:
async function async1() {
  await async2()
  console.log('async1 end')
console.log('async1+1nd')
}
async function async2() {
  console.log('async2 end')
}
console.log('async')
async1()
console.log('async center')
结果:
 
async函数的返回值总是一个Promise对象, await 操作符后的函数为同步方法,下面的为Promise对象的.then的回调。
 
 
本文是对运行机制的一些理解总结,借鉴了一些博主的文章片段,由于内容为早期笔记本留存汇总,不能说明借鉴出处,望海涵。
posted @ 2020-12-29 18:22  李不懂。  阅读(137)  评论(0)    收藏  举报