【Javascript】并发模型和事件循环详解

一、单线程和多线程

关于进程和线程的关系,见博客:

(102条消息) 进程和线程的关系_fengyun_w的博客-CSDN博客_线程和进程的关系

简而言之,资源分配给进程,同一个进程的所有线程共享该进程所有资源;而CPU分配给线程,即真正在处理器运行的是线程。

对JS而言,JS是一个单线程代码,只有一条处理通道,但在任务多的情况下,会出现“假多线程”的情况,这种“多线程”是通过单线程模仿的,也就是假的。对其,我们产生了同步任务和异步任务的概念。

- 同步任务:我们可以理解为:不需要先决条件、不需要等待其他部分执行、直接可以执行的代码部分。

- 异步任务 :反之,需要等待、需要执行条件的代码部分。一般其诞生时不直接进入主线程,而是进入任务队列中等待。

对于下文提到的Promise对象而言,其并非完全为异步任务,一开始新建promise对象时传入的函数方法属于同步任务(因为不要等其他人),而 .then之后执行的内容则属于异步任务

而对于浏览器的渲染进程,其是提供多个线程的,包含如下:

  • JS引擎线程
  • 事件触发线程
  • 定时触发器线程
  • 异步Http请求线程
  • GUI渲染线程

二、事件循环处理机制

  • 宏任务和微任务

在ES3以及以前的版本中,JavaScript本身没有发起异步请求的能力,也就没有微任务的存在。在ES5之后,JavaScript引入了Promise,这样,不需要浏览器,JavaScript引擎自身也能够发起异步任务了。而在ES6 规范中,microtask 称为 jobs,macrotask 称为 task。其中,宏任务是由宿主发起的,而微任务由JavaScript自身发起。

上述宏任务中,script应理解为视为的异步任务部分,而不是同步代码。异步任务才区分宏、微,同步任务则优先执行。

 宏、微任务的执行顺序:

先执行同步代码,遇到异步宏任务则将异步宏任务放入宏任务队列中,遇到异步微任务则将异步微任务放入微任务队列中,当所有同步代码执行完毕后,再将异步微任务从队列中调入主线程执行,微任务执行完毕后再将异步宏任务从队列中调入主线程执行,一直循环直至所有任务执行完毕。

  • 消息队列(任务队列)和事件循环

渲染进程内部会维护多个消息队列,比如延迟执行队列和普通地消息队列,然后主线程采用一个 for 循环,不断地从这些任务队列中取出任务并执行任务。只要主线程空了,就会去读取"任务队列",这就是JavaScript的运行机制。这个过程会不断重复,这种机制就被称为事件循环(event loop)。

三、例题

下述题目中输出顺序为 1 3 6 2 7 8 4 5

详解:

  1. 找到同步任务:第2行、第13行执行的调用进入的async2函数属于同步任务这里await其实是下一条指令console.log(2)陷入等待,而async2是直接执行】、第22行new Promise中直接传入的函数直接执行。所以主线程依次执行同步任务后输出 1 3 6
  2. 在这个过程中,遇到了第5行的await和25、27通过then返回的Promise对象(放入微任务队列),以及对于setTimeOut该具有异步属性的函数(放入宏任务队列)【即使其延迟为0,性能依旧不变】
  3. 所以在同步任务执行完毕后,从微任务中依次取出,输出 2 7 8;然后从宏任务队列中取出setTimeOut执行
  4. 完成一次循环,进入主线程再次优先寻找同步任务,找到15行中的new Promise传递内容,执行完毕,输出4。然后then进入微任务队列,从微任务队列中调出再输出5。至此所有输出完毕。


参考博客:

(101条消息) Event Loop 和消息队列_song-jian的博客-CSDN博客

(102条消息) 什么是宏任务、微任务?宏任务、微任务有哪些?又是怎么执行的?_F N Janine的博客-CSDN博客_宏任务微任务

消息队列和事件循环、宏任务和微任务 - 掘金 (juejin.cn)

猜你喜欢

转载自blog.csdn.net/weixin_57208584/article/details/128705427