为什么js在浏览器中会有事件循环的机制?
- js是单线程的
- JavaScript的主要用途是与用户互动,以及操作DOM。如果它是多线程的会有很多复杂的问题 要处理,比如有两个线程同时操作DOM,一个线程删除了当前的DOM节点,一个线程是要操 作当前的DOM阶段,最后以哪个线程的操作为准?为了避免这种,所以JS是单线程的。即使 H5提出了web worker标准,它有很多限制,受主线程控制,是主线程的子线程。
- event loop 实现非堵塞
事件循环中两种任务
- 宏任务
- 整体代码、setTimeout、setInterval、I/O操作、UI 渲染等
- 微任务
- new Promise().then后面的回调、MutaionObserver(监听事件的改变,前端的回溯)、
为什么要引入微任务的概念,只有宏任务不行吗?
- 宏任务 先进先出的原则执行
- 在js的执行过程中(页面的渲染过程中),他们所有的任务保持着先进先出的原则
- ⻚面渲染事件,各种IO的完成事件等随时被添加到任务队列中,一直会保持先进先出的原则执 行,我们不能准确地控制这些事件被添加到任务队列中的位置。但是这个时候突然有高优先级 的任务需要尽快执行,那么一种类型的任务就不合适了,所以引入了微任务队列。
- 在一个宏任务执行完一遍之后,会把他中途中碰到的微任务已经添加到队列里面去,宏任务执行完之后,去微任务的队列里面,把已经添加进去的微任务执行一遍,在微任务队列里面又碰到新添加的微任务,就继续把它执行,直到结束执行下一个宏任务。
Node中的事件循环和浏览器的时间循环有什么区别?
- timers定时器
- 执行已经安排的setTimeout和 setInterval的回调函数
- pending callback 待定回调
- idle,prepare
- poll
- check
- close callback
- socket.on('close',()=>{})
微任务和宏任务在node中的执行顺序
- Node v10及以前
- 执行完一个阶段中所有任务
- 执行nextTick队列里的任务
- 执行完微任务队列的任务
- Node v10以后
例题1
async function async1(){
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2(){
console.log('async2')
}
console.log('script start')
setTimeout(function(){
console.log('setTimeout')
},0)
async1()
new Promise(function(resolve,reject){
console.log('promise1')
resolve()
}).then(function(){
console.log('promise2')
})
console.log('script end')
答案:
1.script start
2.async1 start
3.async2
4.promise1
5.script end
6.async1 end
7.promise2
8.setTimeout
- 运行整体代码--> 打印script start
- 遇到setTimeout,不执行,放到下一个宏任务执行
- 运行async1函数
- 执行new Promise中
- resolve返回成功,把.then后面的内容再放入第二个微任务里,一会儿执行
- 运行代码 --> 打印 script end
- 整体代码中第一次鞥宏任务执行完毕,开始执行微任务
- 首先运行第一个碰到的微任务--> 打印出 async1 end
- 运行第二个碰到的微任务 --> 打印出 promise2
- 宏任务里的微任务全部运行完毕,开始执行setTimeout -->打印出 setTimeout
例题2
console.log('start')
setTimeout(function(){
console.log('children2')
Promise.resolve().then(()=>{
console.log('children3')
})
},0)
new Promise(function(resolve,reject){
console.log('children4')
setTimeout(function(){
console.log('children5')
resolve('children6')
})
}).then(res=>{
console.log('children7')
setTimeout(()=>{
console.log(res)
})
})
答案:
- 运行整体代码 --> start
- 遇到setTimeout放入下一轮宏任务中
- 运行到new Promise 打印--> children4
- 又遇到setTimeout,放入下下轮宏任务中
- 尝试清空微任务,发现没有微任务
- 注意:.then是在setTimeout之后resolve的,所以.then的回调这个微任务现在还没有触发
- 清空第一个宏任务 打印-->children2
- 尝试清空这一轮宏任务中的微任务:Promise.resolve.then(),打印-->children3
- 继续清空第二个宏任务,resolve有返回值了 打印-->children5
- 尝试清空微任务,打印-->children7
- 打印resolve的返回值 打印-->children6
例题3
const p = function(){
return new Promise((resolve,reject)=>{
const p1 = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(1)
},0)
resolve(2)
})
p1.then((res)=>{
console.log(res)
})
console.log(3)
resolve(4)
})
}
p().then(res=>{
console.log(res)
})
console.log('end')
答案:
- 观察题目,p返回了一个promise,.then后面的等待p有返回之后执行,先放入微任务中
- 打印-->3
- 打印-->end
- p1中返回resolve(2),setTimeout中就不返回了,所以执行微任务.then打印-->2
- 打印-->4