javascript中的事件循环
js引擎
执行一个js
文件时,会创建一个全局执行上下文
,代码依次执行下来,当遇到函数执行时,会再创建一个函数执行上下文
,此函数将压入执行栈中,**函数中的同步任务会依次执行下来,例如console.log(xxx)
**,当函数中又有函数被调用,则再创建新的执行上下文,并把函数压入执行栈中,以此类推。- 事件循环:
console.log('start'); // 1.开始执行同步任务。
Promise.resolve().then(() => { // 2.发现有微任务队列,执行微任务中的代码,同时生成一个宏任务↓。
console.log('Promise1')
setTimeout(() => { // 5.执行最后一个宏任务。
console.log('setTimeout2')
}, 0)
})
setTimeout(() => { // 3.这个宏任务先进队列,先执行,此时宏任务中生成了一个微任务。
console.log('setTimeout1')
/*
* 当外面这个宏任务完成之后
* 会检查微任务队列中有没有没完成的任务
*/
Promise.resolve().then(() => {
console.log('Promise2') // 4. 有,则先完成此微任务再执行宏任务队列中的代码。
})
}, 0)
console.log('end'); // 1.开始执行同步任务,执行完后检查是否有微任务队列。
// start
// end
// Promise1
// setTimeout1
// Promise2
// setTimeout2
setTimeout
执行时,并没有并没有将回调函数放入事件循环队列中,而是在等待时间过去之后才将回调放入,所以,如果此时队列中还有其他任务排在前面,setTimeout
的回调并不会马上执行,这就它会是产生误差的原因。
在当前的微任务没有执行完成时,是不会执行下一个宏任务的。
// 4. 最后执行下一个宏任务
setTimeout(_ => console.log(4))
new Promise(resolve => {
resolve()
/*
* 1.new Promise创建后会立即执行(同步任务)
* script执行也是属于宏任务
* 相当于script宏任务生成的微任务
*/
console.log(1)
}).then(_ => {
/*
* 3.执行Promise后生成的微任务
* 当前的宏任务生成的微任务没有执行完之前
* 是不会执行下一个宏任务的
*/
console.log(3)
})
console.log(2) // 2.然后执行其它同步任务
Nodejs中的事件循环
阶段概述
timer
(定时器):本阶段执行已经被setTimeout()
和setInterval()
的调度回调函数。I/O callbacks
(待定回调):处理一些上一轮循环中的少数未执行的I/O
回调。idle,perpare
:仅系统内部使用。poll
(轮询):检索新的I/O
事件;执行与I/O
相关的回调(几乎所有情况下,除了关闭的回调函数,那些由计时器和setImmediate()
调度的之外),其余情况node
将在适当的时候在此阻塞。check
(检测):setImmediate()
回调函数在这里执行。close callbacks
(关闭的回调函数):一些关闭的回调函数,如:socket.on('close', ...)
。
Node规定,process.nextTick
和Promise
的回调函,追加在本轮循环,及同步任务一旦执行完成,就开始执行他们。而setTimeout
、setInterval
、setImmediate
的回调函数,追加在次轮循环。
例子:
// nodejs
console.log('start') // 1.首先执行同步代码
setTimeout(() => {
console.log('timer1') // 3.执行timer中的代码,并且生成的微任务推入微任务队列
Promise.resolve().then(function () {
console.log('promise1') // 5.timer执行完成之后执行微任务队列中的代码
})
}, 0)
setTimeout(() => {
console.log('timer2') // 4.timer中的代码都会同步执行,并将微任务推入微任务队列
Promise.resolve().then(function () {
console.log('promise2') // 6.执行微任务中的代码
})
}, 0)
Promise.resolve().then(function () {
console.log('promise3') // 2.然后执行script宏任务生成的微任务
})
console.log('end') // 1.首先执行同步代码
// start
// end
// promise3
// timer1
// timer2
// promise1
// promise2
// browser
console.log('start') // 1.执行同步任务
setTimeout(() => {
console.log('timer1') // 3.再执行宏任务
Promise.resolve().then(function () {
console.log('promise1') // 4.执行宏任务中创建的微任务
})
}, 0)
setTimeout(() => {
console.log('timer2') // 5.执行事件队列中的其它任务
Promise.resolve().then(function () {
console.log('promise2') // 6.执行宏任务中的微任务
})
}, 0)
Promise.resolve().then(function () {
console.log('promise3') // 2.执行script宏任务创建的微任务
})
console.log('end') // 1.执行同步任务,并将setTimeout放入事件队列
// start
// end
// promise3
// timer1
// promise1
// timer2
// promise2
timer(定时器)
计时器指定可以执行所提供回调 的阈值,而不是用户希望其执行的确切时间。在指定的一段时间间隔后, 计时器回调将被尽可能早地运行。但是,操作系统调度或其它正在运行的回调可能会延迟它们。
计时器只是指定一个阈值,处理其它任务超过了这个阈值是可能的,其它任务完成后系统只能尽可能早的去运行定时任务。
简单的讲就是,在
node.js
启动时,创建了一个类似while(true)
的循环体,每次执行一次循环体称为一次tick
,每个tick
的过程就是查看是否有事件等待处理,如果有,则取出事件极其相关的回调函数并执行,然后执行下一次tick
。所以,有如下代码:A(); B(); C();
它的执行逻辑是,先询问事件观察者当前是否有任务需要执行?观察者回答“有”,于是取出
A
执行,A
是否有回调函数?如果有(如果没有则继续询问当前是否有任务需要执行),则取出回调函数并执行(注意:回调函数的执行基本都是异步的,可能不止一个回调),执行完回调后通过某种方式通知调用者,我执行完了,并把执行结果给你,你自己酌情处理吧,主函数不需要不断询问回调函数执行结果,回调函数会以通知的方式告知调用者我执行完了(don’t call me ,i will call you.),而这个过程主线程并不需要等待回调函数执行完成,它会继续向前执行,即再次询问观察者当前是否还有任务需要执行,重复上面的步骤。。。直到观察者回答没有了,线程结束。
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!