了解这个问题之前,需要先了解一下以下问题:
- 什么是进程?什么是线程?二者有啥关联?
- 任务队列是什么?宏任务?微任务?eventloop?
- 为什么说js是单线程,为什么要设计成单线程?
一,什么是进程?什么是线程?二者有啥关联?
进程:CPU资源分配的最小单位;浏览器是多进程的,每个tab页都是一个进程;
线程:CPU调度的最小单位;一个进程中可以有多个线程;
举例:进程就是一个公司,每个公司都有自己的资源进行调度,公司之间是相互独立的;而线程就是公司的每个员工,多人协作完成任务,员工之间共享公司的空间;
二,事件循环,宏任务、微任务
所有的任务都可以分为同步任务、异步任务;同步任务,就是立即执行的任务,一般会进入主线程中执行;异步任务,比如promise、ajax网络请求、setTimeout函数等都属于异步任务,会通过任务队列的先进先出的原则来进行协调;
事件循环(Event Loop):主线程内的任务执行完之后,会去任务队列读取任务推入主线程,以此循环,直到任务全部执行完成。
异步任务分类:宏任务(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
三、为什么说js是单线程,为什么要设计成单线程?
js设计为单线程是跟它的用途有关,因为js作为浏览器的脚本语言,主要就是实现用户与浏览器的交互以及操作DOM,这就决定了js只能是单线程,要不然就会带来很复杂的同步问题,比如:假如js是多线程,如果一个线程要修改某个DOM,而另外一个线程要删除这个DOM,此时浏览器就不知道该如何操作了,一脸茫然;所以为了避免复杂性,从诞生开始就是单线程。