为什么任务要分同步和异步?宏任务与微任务又有何区别? (结合面试题理解)
JavaScript是单线程的,也就是说,同一个时刻,JavaScript只能执行一个任务,其他任务只能等待。
1.为什么JavaScript是单线程的?
js是运行于浏览器的脚本语言,因其经常涉及操作dom,如果是多线程的,也就意味着,同一个时刻,能够执行多个任务。
试想,如果一个线程修改dom,另一个线程删除dom,那么浏览器就不知道该先执行哪个操作。
所以js执行的时候会按照一个任务一个任务来执行。
2.为什么任务要分为同步任务和异步任务?
【为什么任务要分同步和异步?宏任务与微任务又有何区别? (结合面试题理解)】试想一下,如果js的任务都是同步的,那么遇到定时器、网络请求等这类型需要延时执行的任务会发生什么?
页面可能会瘫痪,需要暂停下来等待这些需要很长时间才能执行完毕的代码
所以,又引入了异步任务。
- 同步任务:同步任务
不需要等待
可立即看到执行结果,比如console - 异步任务:异步任务
需要等待
一定的时候才能看到结果,比如setTimeout、网络请求
任务(代码) | 宏/微 任务 | 环境 |
---|---|---|
script | 宏任务 | 浏览器 |
事件 | 宏任务 | 浏览器 |
网络请求(Ajax) | 宏任务 | 浏览器 |
setTimeout() 定时器 | 宏任务 | 浏览器 / Node |
fs.readFile() 读取文件 | 宏任务 | Node |
Promise.then() | 微任务 | 浏览器 / Node |
比如去银行排队办业务,每个人的业务就相当于是一个宏任务;
比如一个人,办的业务有存钱、买纪念币、买理财产品、办信用卡,这些就叫做微任务。
文章图片
执行顺序:
文章图片
概念
1.宏任务:当前调用栈中执行的代码成为宏任务。(主代码快,定时器等等)。
2.微任务: 当前(此次事件循环中)宏任务执行完,在下一个宏任务开始之前需要执行的任务,可以理解为回调事件。(promise.then,proness.nextTick等等)。
3.宏任务中的事件放在callback queue中,由事件触发线程维护;微任务的事件放在微任务队列中,由js引擎线程维护。
事件循环(Event Loop)
事件循环就是一个在 "JavaScript 引擎等待任务","执行任务"和"进入休眠状态等待更多任务"这几个状态之间转换的无限循环。
引擎的一般算法:
- 当有任务时:
- 从最先进入的任务开始执行。
- 休眠直到出现任务,然后转到第 1 步。
1.
console.log(1)setTimeout(function() { // 定时器是宏任务
console.log(2)
}, 0)const p = new Promise((resolve, reject) => {
resolve(1000)// 微任务
})
p.then(data => {
console.log(data)
})console.log(3)// 运行结果: 1, 3, 1000, 2
面试题分析:
先分析有几次事件循环? 有两次事件循环:第一次先运行script标签里面的内容,在执行栈中运行后,先打印的是 1, 3; 在运行过程中遇到的微任务是要加到微任务队列里面等待,当执行栈中的任务运行完后,在执行微任务,即打印 1000,此时第一次循环结束,第二次循环在执行栈中运行定时器,则最总输出结果是:1, 3, 1000, 2
2.
console.log(1)
setTimeout(function() {
console.log(2)
new Promise(function(resolve) {
console.log(3)
resolve()
}).then(function() {
console.log(4)
})
})new Promise(function(resolve) {
console.log(5)
resolve()
}).then(function() {
console.log(6)
})
setTimeout(function() {
console.log(7)
new Promise(function(resolve) {
console.log(8)
resolve()
}).then(function() {
console.log(9)
})
})
console.log(10)// 运行结果 : 1 5 10 6 2 3 4 7 8 9
3.
console.log(1)setTimeout(function() {
console.log(2)
}, 0)const p = new Promise((resolve, reject) => {
console.log(3)
resolve(1000) // 标记为成功
console.log(4)
})p.then(data => {
console.log(data)
})console.log(5)// 运行结果: 1 3 4 5 1000 2
4.
setTimeout(() => {
console.log(1)
}, 0)
new Promise((resolve, reject) => {
console.log(2)
resolve('p1')new Promise((resolve, reject) => {
console.log(3)
setTimeout(() => {
resolve('setTimeout2')
console.log(4)
}, 0)
resolve('p2')
}).then(data => {
console.log(data)
})setTimeout(() => {
resolve('setTimeout1')
console.log(5)
}, 0)
}).then(data => {
console.log(data)
})
console.log(6)// 运行结果: 2 3 6 p2 p1 1 4 5
文章图片
可以做一下题,加强理解
推荐阅读
- 我要做大厨
- 为什么你的路演总会超时()
- 这辈子我们都不要再联系了
- 眼光要放高远
- 财商智慧课(六)
- 我们重新了解付费。
- 吃了早餐,反而容易饿(为什么?)
- 我要我们在一起(二)
- 螃蟹和这些食物同吃,轻则腹泻、重则中毒!要小心哦~
- 我执意要等,是因为我相信你一定会来