js单线程
JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊。
JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。
为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。
任务队列
单线程就意味着,所有的任务都要排队执行,前一个任务结束,再会执行后一个任务,如果前一个任务没有结束,那么后一个任务不得不等前一个任务执行完。
主线程和任务队列示意图
任务
任务一般分为同步任务,另一种是异步任务,同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才会执行后一个任务,异步任务指的是:不进入主线程,而是进入‘任务队列’的任务,只有主线程的任务执行完毕才会执行异步任务
那么请看一下例子
console.log('start');
Promise.resolve().then(()=>{
setTimeout(() => {
console.log(1);
}, 0);
console.log(2);
}).then(()=>{
console.log(3);
})
console.log(4);
setTimeout(()=>{
console.log(5);
},0)
console.log('end');
//先同步,再异步
//console.log('start');因为promise是异步任务,所以先执行 console.log(4),然后继续向下执行
//console.log('end'),至此同步任务执行完毕,然后执行异步任务promise,因为promise里面的
//setTimeout是异步任务,所以会放到异步任务队列里,优先执行console.log(2),继续执行then,然后
//执行setTimeout,最后在执行promise里面的setTimeout
//所以执行结果为 start 4 end 2 3 5 1
事件循环
概念
JavaScript 有一个基于事件循环的并发模型,事件循环负责执行代码、收集和处理事件以及执行队列中的子任务。这个模型与其它语言中的模型截然不同,比如 C 和 Java。(摘自MDN)
简单地说,对于 JS 运行中的任务,JS 有一套处理收集,排队,执行的特殊机制,我们把这套处理机制称为事件循环(Event Loop)。
微任务、宏任务
微任务主要有:promise
宏任务主要有:setTimeout 、setInterval
注意:先执行微任务,在执行宏任务,与位置无关
举一个小例子
下面一段代码执行结果是什么?
console.log('start');
setTimeout(() => {
console.log(1);
}, 0)
console.log(2);
Promise.resolve().then(() => {
setTimeout(() => {
console.log(3);
}, 0);
console.log(4);
}).then(() => {
console.log(5);
})
//promise一旦创建立即执行
new Promise((resolve,reject)=>{
console.log(6);
})
console.log('end');
上面一段代码的执行结果为:
start
2
6
end
4
5
1
3