进程 & 线程
- 进程指的是cpu运行指令和保存上下文所需的时间
- 线程是进程中更小的单位,指的是一段指令执行所需的时间
具体介绍
JavaScript 是一种单线程编程语言,这意味着它在任何特定时刻只能执行一个任务。单线程的 JavaScript 在浏览器中运行在主线程上,处理诸如用户交互、DOM 操作、网络请求等任务。
虽然 JavaScript 本身是单线程的,但是浏览器是多进程的。在浏览器中,每个标签页通常都会有一个独立的渲染进程,用于处理页面的渲染和布局。此外,浏览器也可能有其他进程,如网络请求进程、插件进程等,用于处理不同的任务。
在 JavaScript 中,虽然主线程是单线程的,但是它通过事件循环和异步任务的机制实现了并发和非阻塞的效果。例如,通过使用 Web API(如 setTimeout、fetch 等)可以实现异步操作,当需要进行长时间的计算或者网络请求时,JavaScript 可以委托给浏览器的其他进程来处理,从而避免阻塞主线程,保持页面的响应性。
对于线程,JavaScript 运行在浏览器的主线程上,但在某些情况下,浏览器也可以使用 Web Worker 来创建额外的线程,用于执行一些耗时的计算,以避免阻塞主线程。Web Worker 是浏览器提供的一种机制,允许 JavaScript 创建一个新的线程来执行一些耗时的任务,从而不影响主线程的执行。
总之,虽然 JavaScript 本身是单线程的,但是浏览器是多进程的,JavaScript 通过事件循环和异步任务实现了并发效果,在某些情况下也可以利用 Web Worker 来创建额外的线程进行并行处理。这些机制使得 JavaScript 能够更高效地处理各种任务,并提供更好的用户体验。
浏览器新开一个tap页(进程)
当在浏览器中打开一个新的标签页时,会涉及到多个线程和进程来处理不同的任务。下面是与 JavaScript 相关的一些线程和进程:
-
渲染线程 :每个标签页通常都有一个独立的渲染进程,用于处理页面的渲染和布局。渲染线程负责解析 HTML、CSS,并将其转换为可视化的网页。在渲染过程中,如果遇到 JavaScript 代码,它会暂停渲染并将控制权交给 JavaScript 引擎线程。
-
JavaScript 引擎线程 :JavaScript 引擎线程负责执行 JavaScript 代码。当渲染线程遇到需要执行的 JavaScript 代码时,它会将代码传递给 JavaScript 引擎线程进行处理。JavaScript 引擎线程按照代码的顺序执行,但是在执行耗时操作时,会通过异步机制将控制权交还给渲染线程,以保持页面的响应性。
-
HTTP 请求线程 :当网页需要发起网络请求(例如获取数据、加载资源)时,浏览器会创建一个独立的 HTTP 请求线程来处理这些请求。HTTP 请求线程负责建立连接、发送请求和接收响应等网络操作。当响应返回后,它会通过事件循环机制将数据传递给 JavaScript 引擎线程进行后续处理。
这些线程和进程之间通过进程间通信(IPC)来交互,并共同协作完成网页的渲染和功能执行。它们在浏览器中运行并配合工作,以提供良好的用户体验。
需要注意的是,不同浏览器可能在多进程架构上有所不同,因此具体的实现细节可能会有所差异。以上介绍是一般情况下的工作原理,但具体的实现可能因浏览器版本、操作系统等因素而有所不同。
js是单线程的
JavaScript 作为一种单线程语言,具有一些优点:
-
节约内存:由于 JavaScript 是单线程的,因此在执行环境中只需要一个调用栈(call stack),这样可以节约内存空间。相比之下,多线程程序可能需要为每个线程分配独立的调用栈和上下文,消耗更多的内存资源。
-
没有锁的概念,节约了上下文切换的时间:在多线程编程中,为了防止多个线程同时访问共享的资源而导致数据混乱,通常需要使用锁(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 运行时环境中的异步任务能够以合适的顺序执行,并且不会阻塞主线程,保持了页面的响应性。
事件循环的基本流程可以总结如下:
- 执行同步代码(宏任务) :首先执行当前调用栈中的所有同步任务,这些任务属于宏任务。
- 查询是否有异步任务需要执行:当执行栈空闲时,会查询是否有待执行的异步任务,如果有则将其放入任务队列中等待执行。
- 执行微任务 :在当前宏任务执行结束后,会立即执行所有微任务。微任务包括 Promise 的
then()
方法、MutationObserver 回调等。 - 渲染页面:如果需要更新页面,会进行页面重绘(rendering)操作。
- 执行宏任务:开始执行下一次事件循环的宏任务,重复上述过程。
这个过程形成了一个循环,不断地处理宏任务、微任务和页面渲染,保证了 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