浅析JavaScript异步到底是怎么实现的?


浅析JavaScript异步到底是怎么实现的?

文章插图

一直以来都知道用JAVAScript是一门单线程语言,在笔试过程中不断的遇到一些输出结果的问题,考量的是对异步编程掌握情况 。一般被问到异步的时候脑子里第一反应就是 Ajax,setTimseout...这些东西 。在平时做项目过程中,基本大多数操作都是异步的 。JavaScript异步都是通过回调形式完成的,开发过程中一直在处理回调,可能不知不觉中自己就已经处在 回调地狱中 。
浏览器线程在开始之前简单的说一下浏览器的线程,对浏览器的作业有个基础的认识 。之前说过 JavaScript是单线程作业,但是并不代表浏览器就是单线程的 。
在 JavaScript引擎中负责解析和执行 JavaScript代码的线程只有一个 。但是除了这个主进程以外,还有其他很多辅助线程 。那么诸如 onclick回调,setTimeout,Ajax这些都是怎么实现的呢?即浏览器搞了几个其他线程去辅助 JavaScript线程的运行 。
浏览器有很多线程,例如:
  1. GUI渲染线程 - GUI渲染线程处于挂起状态的,也就是冻结状态
  2. JavaScript引擎线程 - 用于解析JavaScript代码
  3. 定时器触发线程 - 浏览器定时计数器并不是 js引擎计数
  4. 浏览器事件线程 - 用于解析BOM渲染等工作
  5. http线程 - 主要负责数据请求
  6. EventLoop轮询处理线程 - 事件被触发时该线程会把事件添加到待处理队列的队尾
  7. 等等等
从上面来看可以得出,浏览器其实也做了很多事情,远远的没有想象中的那么简单,上面这些线程中 GUI渲染线程, JavaScript引擎线程, 浏览器事件线程是浏览器的常驻线程 。
当浏览器开始解析代码的时候,会根据代码去分配给不同的辅助线程去作业 。
进程
【浅析JavaScript异步到底是怎么实现的?】进程是指在操作系统中正在运行的一个应用程序
线程
线程是指进程内独立执行某个任务的一个单元 。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈) 。
进程中包含线程,一个进程中可以有N个进程 。我们可以在电脑的任务管理器中查看到正在运行的进程,可以认为一个进程就是在运行一个程序,比如用浏览器打开一个网页,这就是开启了一个进程 。但是比如打开3个浏览器,那么就开启了3个进程 。
同步&异步既然要了解同步异步当然要简单的说一下同步和异步 。说到同步和异步最有发言权的真的就属 Ajax了,为了让例子更加明显没有使用 Ajax举例 。(●ˇ∀ˇ●)
同步
同步会逐行执行代码,会对后续代码造成阻塞,直至代码接收到预期的结果之后,才会继续向下执行 。
console.log(1);alert("同步");console.log(2);// 结果:// 1// 同步// 2异步如果在函数返回的时候,调用者还不能够得到预期结果,而是将来通过一定的手段得到结果(例如回调函数),这就是异步 。
console.log(1);setTimeout(() => { alert("异步");},0);console.log(2);// 结果:// 1// 2// 异步为什么JavaScript要采用异步编程一开始就说过,JavaScript是一种单线程执行的脚本语言(这可能是由于历史原因或为了简单而采取的设计) 。它的单线程表现在任何一个函数都要从头到尾执行完毕之后,才会执行另一个函数,界面的更新、鼠标事件的处理、计时器( setTimeout、setInterval等)的执行也需要先排队,后串行执行 。假如有一段 JavaScript从头到尾执行时间比较长,那么在执行期间任何 UI更新都会被阻塞,界面事件处理也会停止响应 。这种情况下就需要异步编程模式,目的就是把代码的运行打散或者让 IO调用(例如 AJAX)在后台运行,让界面更新和事件处理能够及时地运行 。
JavaScript语言的设计者意识到,这时主线程完全可以不管 IO设备,挂起处于等待中的任务,先运行排在后面的任务 。等到 IO设备返回了结果,再回过头,把挂起的任务继续执行下去 。
异步运行机制:
  1. 所有同步任务都在主线程上执行,形成一个执行栈 。
  2. 主线程之外,还存在一个 任务队列 。只要异步任务有了运行结果,就在 任务队列之中放置一个事件 。
  3. 一旦 执行栈中的所有同步任务执行完毕,系统就会读取 任务队列,看看里面有哪些事件 。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行 。
  4. 主线程不断重复上面的第三步 。

  5. 推荐阅读