关于js单线程的问题
为什么说 js 是单线程?
为了搞清楚这个问题,我们需要先了解这几个问题:
- 什么是线程?什么是进程?他们之间的关系?
- 什么是任务队列( Event Queue ),任务分类(宏任务、微任务)?
- 什么是事件循环?
- 为什么说 js 是单线程?
- 为什么 js 要是单线程?
接下来我们一起来看一下:
- 什么是线程?什么是进程?他们之间的关系?
-
线程定义:线程是 CPU 调度的最小单位
-
进程定义:进程是资源分配的最小单位
抽象不太容易理解,但是有两个关键词:CPU 调度、资源分配
背景:一个系统中,有很多进程,它们都会使用内存。为了确保内存不被别人使用,每个进程所能访问的内存都是圈好的。一人一份,谁也不干扰谁,进程需要管理好它的资源;线程作为进程的一部分,扮演的角色就是怎么利用中央处理器去运行代码。这其中牵扯到的最重要资源的是中央处理器和其中的寄存器,和线程的栈(stack)。这里想强调的是,线程关注的是中央处理器的运行,而不是内存等资源的管理。
总结一下,通过计算机操作系统的角度出发的。进程和线程不是同一个层面上的概念,线程是进程的一部分,线程主抓中央处理器执行代码的过程,其余的资源的保护和管理由整个进程去完成。
- 什么是任务队列( Event Queue ),任务分类(宏任务、微任务)?
定义:所有的任务可以分为同步任务和异步任务,同步任务,顾名思义,就是立即执行的任务,同步任务一般会直接进入到主线程中执行;而异步任务,就是异步执行的任务,比如 ajax 网络请求,setTimeout 定时函数等都属于异步任务,异步任务会通过任务队列的机制(先进先出的机制)来进行协调。
异步任务分类:宏任务(macro-task)和微任务(micro-task)。
执行顺序:如果微任务列表里面有任务 会执行完毕后再执行宏任务。
宏任务主要包含:script( 整体代码)、setTimeout、setInterval、I/O、UI 交互事件、setImmediate(Node.js 环境)
微任务主要包含:Promise、MutaionObserver、process.nextTick(Node.js 环境)
演示代码如下:
// 这是一个同步任务
console.log('1') //--------> 直接被执行
目前打印结果为:1
// 这是一个宏任务
setTimeout(function () { //--------> 整体的 setTimeout 被放进宏任务列表
console.log('2') //目前宏任务列表记为【s2】
});
new Promise(function (resolve) {
// 这里是同步任务
console.log('3'); //--------> 直接被执行
resolve(); //目前打印结果为:1、3
// then 是一个微任务
}).then(function () { //--------> 整体的 then[包含里面的 setTimeout 被放进微任务列表
console.log('4') //目前微任务列表记为【t45】
setTimeout(function () {
console.log('5')
});
});
执行结果为:1、3、4、2、5
- 什么是事件循环?
定义:主线程内的任务执行完毕为空,会去任务队列读取对应的任务,推入主线程执行。 不断重复的过程就是我们说的 Event Loop (事件循环)。
- 为什么说 js 是单线程?
浏览器是多进程的,浏览器每一个 tab 标签都代表一个独立的进程,其中浏览器渲染进程(浏览器内核)属于浏览器多进程中的一种,主要负责页面渲染,脚本执行,事件处理等
其包含的线程有:GUI 渲染线程(负责渲染页面,解析 HTML,CSS 构成 DOM 树)、JS 引擎线程、事件触发线程、定时器触发线程、http 请求线程等主要线程。
主线程:也就是 js 引擎执行的线程,这个线程只有一个,页面渲染、函数处理都在这个主线程上执行。
工作线程:也称幕后线程,这个线程可能存在于浏览器或 js 引擎内,与主线程是分开的,处理文件读取、网络请求等异步事件。
可以看出,异步操作都是放到事件循环队列里面,等待主执行栈来执行的,并没有专门的异步执行线程,所以说 js 是单线程
- 为什么 js 要是单线程?
js 设计为单线程还是跟他的用途有关,试想一下 如果 js 设计为多线程 那么同时修改和删除同一个 dom 浏览器又该如何执行?
相关链接: