JavaScript核心运行机制--单线程、同步和异步、执行栈和任务队列

单线程

JavaScript 的单线程,与它的用途有关。作为浏览器脚本语言,它主要用来实现与用户的一个互动,以及操作 DOM。

这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定 JavaScript 同时有两个线程,一个线程在某个 DOM 节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?

  • 单线程,是指在 JS 引擎中只有一个解释和执行JavaScript代码线程
  • 意味着:在同一时间只能执行一个代码块,这些代码块的执行就阻塞了异步事件的处理

参考文章:千千寰宇–JavaScript 之 JS 单线程|事件循环|事件队列|执行栈

同步和异步

同步任务:

是指在执行栈中的任务,必须等待前一个任务执行完,才执行下一个

  • 但是,当一个同步任务在执行时,它并不会一直占用 CPU,相反,CPU 的占用率是较低的,CPU 的运算性能是远远高于 I/O 设备读取速度的

    为什么?
    因为摩尔定律,处理器性能的发展非常快,但相比之下,由于我们对存储设备有不同的需求:包括要求它访问速度快;又稳定,能够永久保存;同时容量够大
    为了实现多种需求我们搭建了存储系统,包含多种存储设备,分为内存和外存,内存就实现访问快,比如内存条;外存呢就让它稳定,容量大,比如硬盘

  • 因此让 CPU 等待就造成了性能的浪费
  • 为了解决这个问题,就有了异步的概念,将这些需要等待的任务挂起,主线程先运行排在后面的任务。等到 IO 设备返回了结果,再回过头,把挂起的任务继续执行下去

异步任务:

异步任务指的是,不进入主线程、而进入”任务队列”(task queue)的任务,只有”任务队列”通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

常见异步任务:

定时器任务(setTimeout();setInterval();)、Ajax 事件浏览器/用户行为事件(例如:浏览器加载(load)、鼠标单击 click、鼠标滑动/滑过/离开(mouseover、mouseout、mouseleave 等

宏任务和微任务

异步任务分为异步宏任务和异步微任务

常见的宏任务有 setTimeout、setInterval;script 整体代码、I/O、UI 交互

常见的微任务有 Promise.then、process.extTick(node.js 环境)

注意:

  • Promise 中的代码是同步任务,会立即执行,其异步性只体现在**.then****和****catch**
  • **async****声明的函数,会立即执行**
    • 在 async 声明的函数中,await 后面跟的表达式会先执行一遍,然后将其后的其他语句放入微任务队列中,然后直接跳出 async 函数往后执行

执行栈和任务队列

执行栈:管理主线程上同步任务

任务队列:管理异步任务

只要执行栈空了,就会去读取任务队列

举个栗子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
console.log(1); //同步任务A
setTimeout(
//同步任务B
() => {
console.log(2);
}, //任务B产生的异步宏任务
300
);
new Promise((resolve) => { //同步任务C
console.log(3);
resolve(4);
}).then(
//任务C执行过程中resolve(4)语句产生的异步微任务
(num) => {
console.log(num);
}
);
setTimeout(
//同步任务D
() => {
console.log(5);
}, //任务D产生的异步宏任务
800
);

输出顺序:

1
13425

整个执行过程详见:图解 JavaScript 事件循环、执行栈、任务队列、宏任务、微任务
该篇评论区中,

  • 有一个很有意思的比喻–执行栈不像是一个容器,而像是“游标”、一个调用函数
  • 关于“为什么 NextTick 微任务始终在 Promise 微任务之前执行”这一问题的讨论也很深入浅出
PostMan:接口测试工具 Vue2核心原理

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×