js为什么是单线程?
从用途出发,假定js有两个线程,一个添加节点,一个删除这个节点,那么js该怎么运行?
这很矛盾,但确实是不得不考虑的问题
而单线程不存在这样的问题,因为它同一时间只能做一件事,然而效率太低了
所以js作为单线程的语言,也要去考虑如何去实现多线程
js多线程的实现取决于它的运行环境,比如浏览器,浏览器是多线程的,即浏览器开辟了几个线程去辅助js线程的执行
浏览器的五个线程:
js主线程:负责执行栈中栈顶代码的执行
GUI 渲染线程:负责render树的渲染(节点与css样式)
事件监听线程:负责监听事件
以上三个为常驻线程
记时线程:定时器
http异步线程
然而子线程依旧由js主线程控制,本质就是这样了,无法改变
单线程中一个任务执行完后才能执行另一个任务
如果前一个任务耗时很长,那后一个任务就不得不等着,为了解决这种问题任务就分为了两种:同步和异步
同步任务即在主线程上,一个任务执行完后再执行另一个任务
异步任务即不进入主线程,是进入任务队列的任务,当任务队列通知主线程某个异步任务可以执行了,该任务才会进入主线程执行
总体来说,只要主线程空了,就会读取任务队列,不断重复,这就是事件循环
以上并非js运行机制的全部,同步和异步只是广义上的,还要两个更细节的东西
宏任务和微任务
微任务会使程序先放入执行栈中执行
宏任务:整体的script,计时器,事件回调,http回调
微任务:Promise和MutationObsever
实例解析js执行机制
console.log(1);
console.log(2);
console.log(3);
打印顺序为123
这是同步任务,按顺序一步一步执行
console.log(1);
setTimeout(function () {
console.log(2);
},0)
console.log(3);
打印顺序为132
setTimeout为异步任务,异步任务进入异步队列,需要等到同步任务执行完后才会执行任务队列中的任务,即使时间设定为0ms
console.log(1);
setTimeout(function() {
console.log(2)
},0);
new Promise(function(resolve) {
console.log(3);
resolve();
}
).then(function() {
console.log(4)
});
console.log(5);
打印顺序为13542
当涉及到宏任务和微任务时,执行机制就是先执行宏任务再执行微任务,然后执行另一个宏任务,不断循环
script整体作为第一个宏任务进入主线程,遇到console.log输出1,遇到setTimeout放到异步队列,遇到Promise直接执行出3,到这里第一个宏任务并未执行完,遇到console.log(5)时输出5,至此第一个宏任务已经执行完了,而后开始执行微任务then输出4,此时微任务也执行完了,开始执行另一个宏任务中的setTimeout输出2
console.log(1);
setTimeout(function() {
new Promise(function(resolve) {
console.log(6);
resolve();
}
).then(function() {
console.log(7)
});
console.log(2);
},0);
new Promise(function(resolve) {
console.log(3);
resolve();
}
).then(function() {
console.log(4)
});
console.log(5);
这里在上述代码的console.log(2)之前增加了Promise和then
打印顺序为1354627,这里setTimeout内部整体作为宏任务执行,遇到Promise直接输出6,此时宏任务并未执行完,遇到console.log(2)时输出2,至此宏任务结束,执行微任务输出7