看懂JavaScript代码还得从底层开始

进程 & 线程

  • 进程指的是cpu运行指令和保存上下文所需的时间
  • 线程是进程中更小的单位,指的是一段指令执行所需的时间

具体介绍

JavaScript 是一种单线程编程语言,这意味着它在任何特定时刻只能执行一个任务。单线程的 JavaScript 在浏览器中运行在主线程上,处理诸如用户交互、DOM 操作、网络请求等任务。

虽然 JavaScript 本身是单线程的,但是浏览器是多进程的。在浏览器中,每个标签页通常都会有一个独立的渲染进程,用于处理页面的渲染和布局。此外,浏览器也可能有其他进程,如网络请求进程、插件进程等,用于处理不同的任务。

在 JavaScript 中,虽然主线程是单线程的,但是它通过事件循环和异步任务的机制实现了并发和非阻塞的效果。例如,通过使用 Web API(如 setTimeout、fetch 等)可以实现异步操作,当需要进行长时间的计算或者网络请求时,JavaScript 可以委托给浏览器的其他进程来处理,从而避免阻塞主线程,保持页面的响应性。

对于线程,JavaScript 运行在浏览器的主线程上,但在某些情况下,浏览器也可以使用 Web Worker 来创建额外的线程,用于执行一些耗时的计算,以避免阻塞主线程。Web Worker 是浏览器提供的一种机制,允许 JavaScript 创建一个新的线程来执行一些耗时的任务,从而不影响主线程的执行。

总之,虽然 JavaScript 本身是单线程的,但是浏览器是多进程的,JavaScript 通过事件循环和异步任务实现了并发效果,在某些情况下也可以利用 Web Worker 来创建额外的线程进行并行处理。这些机制使得 JavaScript 能够更高效地处理各种任务,并提供更好的用户体验。

浏览器新开一个tap页(进程)

当在浏览器中打开一个新的标签页时,会涉及到多个线程和进程来处理不同的任务。下面是与 JavaScript 相关的一些线程和进程:

  1. 渲染线程 :每个标签页通常都有一个独立的渲染进程,用于处理页面的渲染和布局。渲染线程负责解析 HTML、CSS,并将其转换为可视化的网页。在渲染过程中,如果遇到 JavaScript 代码,它会暂停渲染并将控制权交给 JavaScript 引擎线程。

  2. JavaScript 引擎线程 :JavaScript 引擎线程负责执行 JavaScript 代码。当渲染线程遇到需要执行的 JavaScript 代码时,它会将代码传递给 JavaScript 引擎线程进行处理。JavaScript 引擎线程按照代码的顺序执行,但是在执行耗时操作时,会通过异步机制将控制权交还给渲染线程,以保持页面的响应性。

  3. HTTP 请求线程 :当网页需要发起网络请求(例如获取数据、加载资源)时,浏览器会创建一个独立的 HTTP 请求线程来处理这些请求。HTTP 请求线程负责建立连接、发送请求和接收响应等网络操作。当响应返回后,它会通过事件循环机制将数据传递给 JavaScript 引擎线程进行后续处理。

这些线程和进程之间通过进程间通信(IPC)来交互,并共同协作完成网页的渲染和功能执行。它们在浏览器中运行并配合工作,以提供良好的用户体验。

需要注意的是,不同浏览器可能在多进程架构上有所不同,因此具体的实现细节可能会有所差异。以上介绍是一般情况下的工作原理,但具体的实现可能因浏览器版本、操作系统等因素而有所不同。

js是单线程的

JavaScript 作为一种单线程语言,具有一些优点:

  1. 节约内存:由于 JavaScript 是单线程的,因此在执行环境中只需要一个调用栈(call stack),这样可以节约内存空间。相比之下,多线程程序可能需要为每个线程分配独立的调用栈和上下文,消耗更多的内存资源。

  2. 没有锁的概念,节约了上下文切换的时间:在多线程编程中,为了防止多个线程同时访问共享的资源而导致数据混乱,通常需要使用锁(lock)来进行同步控制。而 JavaScript 作为单线程语言,不存在多线程竞争的情况,因此也就不需要像多线程编程那样频繁地进行锁定和解锁操作,从而节约了上下文切换的时间。

尽管 JavaScript 是单线程的,但通过事件循环和异步任务的机制,它仍能够实现并发效果,处理诸如用户交互、网络请求等任务,保持页面的响应性。此外,JavaScript 也提供了 Web Worker 这样的机制,可以创建额外的线程来执行一些耗时的计算,以避免阻塞主线程。

虽然单线程带来了一些优点,但也意味着 JavaScript 在某些情况下可能无法充分利用多核处理器的优势。因此,在处理大量计算密集型任务时,开发者可能需要考虑使用 Web Worker 或其他技术来进行并行处理,以提高性能和响应速度。 优点:

异步

JavaScript 中的异步编程是其非常重要的特性之一。在 JavaScript 中,异步操作可以帮助我们处理诸如网络请求、定时任务等耗时操作,而不会阻塞主线程,从而保持页面的响应性。

在异步编程中,有两个重要的概念:宏任务(macrotask)和微任务(microtask),它们分别用于管理不同类型的异步操作。

宏任务(Macrotask)

宏任务(macrotask)代表一个离散的、独立的工作单元,在执行过程中不能被打断。常见的宏任务包括:

  • script 执行
  • setTimeout 和 setInterval 回调
  • I/O 操作
  • UI 渲染(UI-rendering)

在事件循环中,每个宏任务会依次被放入任务队列(task queue)中,并在主线程空闲时执行。当一个宏任务开始执行时,它会一直执行到结束,期间不会被其他宏任务打断。

微任务(Microtask)

微任务(microtask)通常比宏任务具有更高的优先级,它们会在每个宏任务执行完毕后立即执行。常见的微任务包括:

  • Promise 的 then() 方法
  • MutationObserver 回调
  • process.nextTick(Node.js 环境)

微任务通常用于执行相对较轻量级的任务,例如处理 Promise 的状态变化、DOM 变动等。由于微任务会在每个宏任务执行完毕后立即执行,因此可以用来确保在宏任务执行前更新状态或执行其他必要的操作。

Event-Loop

JavaScript 中的事件循环(Event Loop)是一种用于管理异步代码执行的机制,它确保了 JavaScript 运行时环境中的异步任务能够以合适的顺序执行,并且不会阻塞主线程,保持了页面的响应性。

事件循环的基本流程可以总结如下:

  1. 执行同步代码(宏任务) :首先执行当前调用栈中的所有同步任务,这些任务属于宏任务。
  2. 查询是否有异步任务需要执行:当执行栈空闲时,会查询是否有待执行的异步任务,如果有则将其放入任务队列中等待执行。
  3. 执行微任务 :在当前宏任务执行结束后,会立即执行所有微任务。微任务包括 Promise 的 then() 方法、MutationObserver 回调等。
  4. 渲染页面:如果需要更新页面,会进行页面重绘(rendering)操作。
  5. 执行宏任务:开始执行下一次事件循环的宏任务,重复上述过程。

这个过程形成了一个循环,不断地处理宏任务、微任务和页面渲染,保证了 JavaScript 引擎在处理异步任务时的顺序和时机。

通过事件循环,JavaScript 能够有效地处理异步任务,保持页面的响应性,并且在需要时及时更新页面。这种机制也为开发者提供了更多处理异步逻辑的方式,使得 JavaScript 在处理复杂的异步场景时更加灵活和高效。

练习

既然学习上面的js执行顺序,那写一个练习:

js 复制代码
console.log('stard');
async function async1() {
  await async2()            //浏览器给await开小灶提速
  console.log('saync1 end');//await把后面的代码放到微任务队列里面
}
async function async2() {
  console.log('saync2 end');
}
async1()
setTimeout(function() {
  console.log('setTimeout');
}, 0)
new Promise((resolve) => {
  console.log('promise');
  resolve()
})
.then(() => {
  console.log('then1');
})
.then(() => {
  console.log('then2');
})
console.log('end'); 

// stard saync2 end promise end saync1 end then1 then2 setTimeout

结合文章和代码的解释你欧克吗?

js 复制代码
async function fn1() {
    await fn2()
    await fn3()
    console.log('fn1 end');
  }
  fn1()
  async function fn2() {
    console.log('fn2 end');
  }
  async function fn3() {
    console.log('fn3 end');
  }
  
  setTimeout(() => {
    new Promise((resolve) => {
      console.log('setTimeout');
      resolve()
    })
    .then(() => {
      console.log('then');
    })
    .then(() => {
      setTimeout(() => {
        console.log('then2 end');
      })
    })
    console.log('setTimeout end');
  }) 

上面欧克那这个应该也没问题

// fn2 end fn3 end fn1 end setTimeout setTimeout end then then2 end

相关推荐
MossGrower6 分钟前
65.Three.js案例-使用 MeshNormalMaterial 和 MeshDepthMaterial 创建 3D 图形
javascript·threejs·spheregeometry·torusknotgeome
蓑笠翁0011 小时前
Python异步编程入门:从同步到异步的思维转变
linux·前端·python
程序员小杰@3 小时前
✨WordToCard使用分享✨
前端·人工智能·开源·云计算
larntin20023 小时前
vue2开发者sass预处理注意
前端·css·sass
Enti7c3 小时前
利用jQuery 实现多选标签下拉框,提升表单交互体验
前端·交互·jquery
SHUIPING_YANG4 小时前
在Fiddler中添加自定义HTTP方法列并高亮显示
前端·http·fiddler
互联网搬砖老肖4 小时前
Web 架构之前后端分离
前端·架构
水银嘻嘻5 小时前
web 自动化之 selenium+webdriver 环境搭建及原理讲解
前端·selenium·自动化
寧笙(Lycode)5 小时前
为什么使用Less替代原始CSS?
前端·css·less
-Camellia007-5 小时前
TypeScript学习案例(1)——贪吃蛇
javascript·学习·typescript