一、JavaScript是单线程
JavaScript是一种单线程的编程语言,意思就是同一时间段只能做一件事,所有任务都需要排队依次完成;
为什么js不能有多个线程呢?
作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准;
所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。
二、同步任务和异步任务
单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。
console.log('1')
setTimeout(function (){
console.log('2')
}, 1000)
console.log('3')
/* 运行结果:
1
3
2
*/
它应该从上到下一行一行执行的,只有当上一行的代码执行完后才会执行下一行代码,为什么这个例子是132而不是123呢?
因为1,3是同步任务而2则是异步任务
- 同步任务:在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
- 异步任务:不进入主线程、而进入"任务队列"的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
三、event loop(事件循环)
执行机制过程如下:
- 所有同步任务都在主线程上执行,形成一个执行栈(调用栈);
- 主线程之外,还存在一个‘任务队列’(task queue),浏览器中的各种 Web API 为异步的代码提供了一个单独的运行空间,当异步的代码运行完毕以后,会将代码中的回调送入到 任务队列中(队列遵循先进先出得原则)
- 一旦主线程的栈中的所有同步任务执行完毕后,调用栈为空时系统就会将队列中的回调函数依次压入调用栈中执行,当调用栈为空时,仍然会不断循环检测任务队列中是否有代码需要执行;
- 这一过程就是我们要了解的event loop(事件循环)机制;
四、宏任务和微任务
异步任务也有区别,分为宏任务和微任务 浏览器中常用的宏任务和微任务:
| 名称 | 事件 | | - | | - | | 宏任务 | setTimeout 、setInterval 、UI rendering | | 微任务 | promise 、requestAnimationFrame
那么异步任务既然分为宏任务和微任务,则队列肯定也分为宏任务队列和微任务队列;
当宏任务和微任务都处于 任务队列中时,微任务的优先级大于宏任务,即先将微任务执行完,再执行宏任务;
console.log('1')
setTimeout(function callback(){
console.log('2')
}, 1000)
new Promise((resolve, reject) => {
console.log('3')
resolve()
})
.then(res => {
console.log('4');
})
console.log('5')
/* 运行结果:
1
3
5
4
2
*/