聊一聊JavaScript中的事件循环

时间:2019-07-14 22:22:32   收藏:0   阅读:84

一、概念:事件循环

JavaScript是单线程的

1、整片 script 整体代码(第一个宏任务)放到执行栈中,执行之后,会触发很多方法

这些方法只能一个个的顺序执行,不能并发

2、这些要执行的方法会放到一个称之为事件队列的地方

3、事件队列又分为宏任务队列和微任务队列,所以要执行的方法会被分类到各自类型的队列列表

4、微任务队列的任务是最优先的,只有微任务队列的任务执行完成后,才会去宏任务队列取队头的第一个任务放到执行栈执行

5、之后可能又产生宏任务和微任务,继续步骤4,微任务优先完成,不断循环下去直到所有任务队列的任务清空

这是整体的一个流程


二、分类

下面分类介绍下:

术语:

1)、执行栈:用来执行当前代码

例子:

各个同步代码

a()

b()

c()

2)、宏任务队列:存储宏任务任务

例子:

script 整体代码

setInterval

setTimeout

I/O

UI 交互

setImmediate

3)、微任务队列:存储微任务任务

例子:

Promise.then

process.nextTick

4)、异步请求未完成挂起队列:存储异步耗时任务

例如:未完成的 ajax 请求被挂起


三、流程:

浏览器解析脚本---

解析到的同步代码按照顺序推入执行栈中---

1、顺序执行执行栈中的所有同步代码---

2、执行完执行栈中的所有同步代码,产生 宏任务队列 微任务队列 异步请求未完成挂起队列---

3、此时执行栈为空,查看微任务队列是否有任务?有执行!当次微任务队列全部执行完,微任务队列为空,才去查看宏任务队列

4、没有则直接查看宏任务队列是否有任务,有,取出宏任务队列最前面的任务推入执行栈---

注:在执行微任务宏任务的过程中,异步请求未完成挂起队列中的任务可能已经完成,完成后会推入宏任务队列

1、顺序执行执行栈中的所有同步代码---如此循环


四、练习例子

例子一

// 请写出输出内容 --- 网上搜出来的一个例子
async function async1() {
  console.log('async1 start') // 2
  await async2()
  console.log('async1 end') // 6
}
async function async2() {
  console.log('async2') // 3
}

console.log('script start') // 1

setTimeout(function() {
  console.log('setTimeout') // 8
}, 0)

async1()

new Promise(function(resolve) {
  console.log('promise1') // 4
  resolve()
}).then(function() {
  console.log('promise2') // 7
})
console.log('script end') // 5

// 输出结果:
// script start
// async1 start
// async2
// promise1
// script end
// async1 end
// promise2
// setTimeout

解析:
第一轮:解析整个代码片段,相当于第一个宏任务在执行栈中执行
此时状态:
执行栈:整片代码段
宏任务队列:null
微任务队列:null

执行过程:
1、遇到 console.log('script start') 直接执行
2、遇到 setTimeout(function() {
  console.log('setTimeout')
}, 0) 
获得一个宏任务 
`function() {
  console.log('setTimeout')
}`
3、遇到 async1(),执行后
解析到 console.log('async1 start') 直接执行
然后解析到 await async2()
执行 async2() 后,遇到 console.log('async2') 直接执行
上面执行完成后,await 之后的动作作为一个微任务 `console.log('async1 end')` 被收集
4、遇到 new Promise
直接执行 console.log('promise1'),然后获取一个微任务 `console.log('promise2')`
5、最后直接执行 console.log('script end')

执行后状态:
执行栈:null
宏任务队列: 
`function() {
  console.log('setTimeout')
}`
微任务队列
`console.log('async1 end')`
`console.log('promise2')`


第二轮:查看执行微任务队列,然后是宏任务队列
此时状态(即上一轮执行后的状态):
执行栈:null
宏任务队列: 
`function() {
  console.log('setTimeout')
}`
微任务队列
`console.log('async1 end')`
`console.log('promise2')`

执行过程:
1、先看微任务队列是否有任务,先清空微任务队列
将 console.log('async1 end') 拿到执行栈执行
执行完成后 将 console.log('promise2') 拿到执行栈执行
执行完成,执行栈此时为空
2、此时微任务队列都执行完成了,执行栈也为空,从宏任务队列队头取第一个任务
将 function() {
  console.log('setTimeout')
} 拿到执行栈执行
执行完成,所有任务都清空,程序结束


例子二

setTimeout(() =>{
    console.log(1)
    setTimeout(() =>console.log(2))
    setTimeout(() =>console.log(3))
    Promise.resolve().then(() => console.log('a'))
    .then(() => console.log('b'))
    .then(() => console.log('c'))
    .then(() => console.log('d'))
})
// 输出结果:1 a b c d  2 3

解析:
1、执行整片代码,产生一个宏任务,没有微任务
宏任务:
`() =>{
    console.log(1)
    setTimeout(() =>console.log(2))
    setTimeout(() =>console.log(3))
    Promise.resolve().then(() => console.log('a'))
    .then(() => console.log('b'))
    .then(() => console.log('c'))
    .then(() => console.log('d'))
    .then(() => console.log('e'))
}`
2、将宏任务拿到执行栈执行,陆续执行里面的代码
产生两个宏任务和一个微任务
宏任务:
`() =>console.log(2)`
`() =>console.log(3)`
微任务:
`() => console.log('a')`
3、将一个微任务拿到执行栈执行
此时又产生一个微任务
宏任务:
`() =>console.log(2)`
`() =>console.log(3)`
微任务:
`() => console.log('b')`
4、将一个微任务拿到执行栈执行
此时又产生一个微任务
宏任务:
`() =>console.log(2)`
`() =>console.log(3)`
微任务:
`() => console.log('c')`
5、将一个微任务拿到执行栈执行
此时又产生一个微任务
宏任务:
`() =>console.log(2)`
`() =>console.log(3)`
微任务:
`() => console.log('d')`
6、将一个微任务拿到执行栈执行
微任务执行完成,取宏任务队列队头第一个任务
7、执行 `() =>console.log(2)` 完后,没有产生微任务,取第二个任务
8、执行 `() =>console.log(3)` 完后,没有任务,程序结束

原文:https://www.cnblogs.com/linjunfu/p/11185776.html

评论(0
© 2014 bubuko.com 版权所有 - 联系我们:wmxa8@hotmail.com
打开技术之扣,分享程序人生!