前言
JavaScript 是一门单线程语言,这意味着它一次只能执行一个任务。这是因为 JavaScript 在设计之初就是作为浏览器脚本语言而诞生的,它的主要用途是与用户交互、操作 DOM 等。如果 JavaScript 是多线程的话,那么可能会导致对共享数据的操作变得复杂,容易出现竞争条件(race condition)等问题。JS中出现了同步和异步。那么接下来介绍一下同步和异步。
同步(Synchronous)执行: 在同步执行中,代码会按照顺序一行一行地执行,每行代码都必须等待上一行代码执行完成后才能继续执行。这意味着如果某个操作需要花费很长时间,那么整个程序的执行会被阻塞,直到该操作完成。
javascript
console.log('开始');
// 执行一个耗时的操作
for (let i = 0; i < 1000000000; i++) {
// 做一些耗时的计算
}
console.log('结束');
上面的例子中,当执行耗时的操作时,JavaScript 引擎会一直忙于计算,直到计算完成后才会继续执行下一行代码。
异步(Asynchronous)执行 : 在异步执行中,代码不会等待某个操作完成才继续执行,而是先将这个操作交给其他部件处理,自己继续执行其他代码。当操作完成后,通过回调函数或者 Promise 等机制来通知执行结果。
javascript
console.log('开始'); // 执行一个异步操作
setTimeout(function()
{ console.log('异步操作完成'); }, 1000);
console.log('结束');
进程与线程
进程(Process):
- 一个进程代表着系统中运行的一个程序实例,它拥有独立的内存空间、文件描述符、以及其他系统资源。
- 每个进程都是相互独立的,它们之间不能直接共享内存数据,而需要通过进程间通信的方式来进行数据交换。
线程(Thread):
- 线程是进程中的一个实体,它可以被系统调度并执行。一个进程中可以包含多个线程,这些线程共享同一进程的内存空间和系统资源。
- 在浏览器中,JavaScript 代码是运行在一个单独的线程中,这个线程被称为"JavaScript 引擎线程"。JavaScript 引擎线程负责执行 JavaScript 代码,并且处理与 DOM、CSSOM 相关的操作。
除了 JavaScript 引擎线程之外,浏览器还有其他一些线程,包括:
- GUI 渲染线程:负责渲染页面,处理与页面 UI 相关的操作。
- 事件触发线程:负责管理用户交互事件,比如鼠标点击、键盘输入等。
- 定时触发线程:负责处理定时器,以及一些与时间相关的操作。
- 异步 HTTP 请求线程:负责处理异步请求(如 Ajax 请求)的回调函数。
JS是单线程的优点
优点:
-
节约内存
-
没有锁的概念,节约上下文切换的时间
Event Loop
事件循环(Event Loop)是一种用于处理和调度异步操作的机制。它是在单线程环境下实现并发的关键组件。在一个应用程序中,可能存在多个异步任务,例如网络请求、文件读写操作等。这些任务不能立即完成,而是需要一定的时间。传统的编程模型中,我们会使用回调函数或者线程来处理这些异步任务,但是这样容易导致代码复杂、难以维护。
事件循环通过将所有的异步任务添加到一个任务队列中,并按照顺序依次执行这些任务,从而实现了异步操作的调度。它采用了事件驱动的方式,当某个任务完成时,会触发相应的回调函数。事件循环不断地从任务队列中取出任务执行,直到所有任务都完成。
微任务(microtask)和宏任务(macrotask)是指事件循环中的两种不同类型的任务。
- 宏任务 (macrotask):script(整体代码)、setTimeout、setInterval、setImmediate'I/O、UI-rendering交互事件
- 微任务 (microtask):Promise.then、MutationObserver,process.nextTick()
# 事件循环Event Loop执行机制
- 执行同步代码(这个属于宏任务)
- 当执行栈为空时,查询是否有异步需要执行
- 执行微任务
- 如果有需要,会渲染页面
- 执行宏任务( 下一次event-loop)
我来测试一下## Event Loop执行机制
javascript
console.log(1);//1
setTimeout(() => {
console.log(2);
new Promise((resolve) => {
console.log(4);
resolve()
setTimeout(() => {
console.log(6);
})
}).then(() => {
console.log(5);
})
}, 1000)
console.log(3);
第一步执行同步代码,打印1,setTimeout是宏任务,要放进宏任务栈里面去,在打印3 第二部没有微任务,在开始一个循环执行打印2 在打印4 在打印 5 在打印6
注意选项
await
javascript
console.log('stard');
async function async1() {
await async2()//浏览器给await开小灶啦
console.log('saync1 end');// 被await挤入微任务
}
async function async2() {
console.log('saync2 end');
}
async1()
await右边的表达式会立即执行,浏览器给await开小灶啦,表达式之后 被await挤入微任务, await微任务可以转换成等价的promise微任务分析
执行同步代码(这个属于宏任务)
xml
<script>
console.log('script start');
setTimeout(function () {
console.log('setTimeout')
}, 1000);
console.log('script ');
</script>
<script>
console.log('script end');
</script>
打印的结果为 'script start' ** 'script '** 'script end' 'setTimeout' 看到这里 大家应该把事件循环学的差不多啦吧 我来出一道题给大家试一下
大家把答案写在评论区吧 喜欢的来个关注 点赞 这个也是以后写文章的动力所在 谢谢大家能观看我的文章 咱下期再见 拜拜