本节目标
- 介绍
- 宏任务/微任务
- Promise.all()
介绍
概念
事件循环(EventLoop)负责执行代码, 收集和处理事件以及执行队列中的子任务
原因
JS是单线程语言, 要防止耗时代码阻塞主线程的任务, 就设计了事件循环模型
过程
- 同步任务直接在调用栈中执行
- 异步任务交给宿主环境(浏览器)执行, 执行完毕后, 加入到任务队列中排队
- 一旦调用栈空闲, 就会读取任务队列中的任务, 继续执行
案例
使用事件循环模型, 分析代码执行过程
<script>
/**
* 目标:阅读并回答执行的顺序结果
* 1 5 3 2 4 点击document打印6
*/
console.log(1)
setTimeout(() => {
console.log(2)
}, 0)
function myFn() {
console.log(3)
}
function ajaxFn() {
const xhr = new XMLHttpRequest()
xhr.open('GET', 'http://hmajax.itheima.net/api/province')
xhr.addEventListener('loadend', () => {
console.log(4)
})
xhr.send()
}
for (let i = 0; i < 1; i++) {
console.log(5)
}
ajaxFn()
document.addEventListener('click', () => {
console.log(6)
})
myFn()
</script>
宏任务/微任务
ES6之后引入了Promise对象, 让JS引擎也可以发起异步任务
异步任务
- 宏任务: 由浏览器环境执行的异步代码
- 微任务: 由JS引擎环境执行的异步代码
(Promis本身是同步任务, 但是then和catch回调函数是异步任务)
执行顺序
<body>
<script>
/**
* 目标:阅读并回答打印的执行顺序
* 1, 3, 5, 4, 2
*/
console.log(1)
setTimeout(() => {
console.log(2)
}, 0)
const p = new Promise((resolve, reject) => {
console.log(3);
resolve(4)
})
p.then(res => {
console.log(res)
})
console.log(5)
</script>
</body>
解释
- 首先执行<script></script>脚本(宏任务)
- 首先执行console.log(1) (同步任务)
- setTimeout()被宿主环境执行, 执行完成后推入宏任务队列
- 执行new Promise() (本身是同步任务), 打印3
- resolve(4)被JS引擎执行, 执行完成后推入微任务队列, 最终打印4
- 执行 console.log(5)
- 微任务队列清空后, 才会执行宏任务列表
- 最终结果 1, 3, 5, 4, 2
经典面试题
<body>
<script>
// 目标:回答代码执行顺序
// 同步
// 微任务
// 宏任务
// 结果 1,7,5,6,2,3,4
console.log(1)
setTimeout(() => {
console.log(2)
const p = new Promise(resolve => resolve(3))
p.then(result => console.log(result))
}, 0)
const p = new Promise(resolve => {
setTimeout(() => {
console.log(4)
}, 0)
resolve(5)
})
p.then(result => console.log(result))
const p2 = new Promise(resolve => resolve(6))
p2.then(result => console.log(result))
console.log(7)
</script>