前言
我在之前的文章中写过异步(Promise:解决JavaScript异步编程难题),js是一门单线程语言,所以在程序的运行中,会有许多的异步,那么一串串代码着实让人摸不着头脑:到底谁先运行?那么本文将带领掘友们来深度剖析异步编程的运行顺序。
进程和线程
说到js是一门单线程语言,那么什么是线程?什么又是进程
- 进程:CPU运行指令和保存上下文所需要的时间
- 线程:进程中的更小的单位,描述了一段指令执行所需的时间
我们来说说线程
js
let a = 1
console.log(a);
setTimeout(()=>{
console.log(a);
},1000)
// let b = 2
for(let i = 0;i<100000;i++){
console.log(i);
}
我们想想这段代码中的定时器什么时候输出。
会在 for 循环正在运行但是经过1s后执行吗?
还是等for 循环运行完之后再执行?
答案是后者。因为js是一门单线程语言,所以在他的线程中只能同时运行一个线程,如果在for 循环运行的同时输出 a,那就变成了多线程,例如Java等语言可以实现这个效果。
优点
- 节约内存开销
- 单线程没有锁的概念,节约上下文切换时间
异步的两种任务
上次我们提到了如何解决异步,这里我们再来聊聊异步的两种类型
宏任务
宏任务是指执行过程相对较长,会占用较多的时间的任务,让我们来列举一下:
- script
- setTimeout
- setInterval
- setImmediate
- I/O
- UI-remdering
微任务
与宏任务相反,微任务是优先级较高的任务,一般会优先执行:
- promise.then()
- MutationObserver()
- Process.nextTick()
事件循环机制(event-loop)
看到这里,我们终于来到了本文的重点!!! 我们先给大家展示优先事件循环的规则:
1.执行同步代码(这属于宏任务)
2.当调用栈为空,查询是否有异步代码需要执行
3.若有,就执行微任务
4.如果有需要,会渲染页面
5.执行宏任务(这也叫下一轮event-loop的开启)
掘友们看光看这段可能会说:这是啥呀?我也看不懂。
简而言之就是先执行同步代码,
要是遇上微任务,就存入微任务队列,遇上宏任务就存入宏任务队列
执行完同步代码,就开始依次执行微任务队列
执行完微任务队列,就开始执行宏任务队列
那我们话不多说上代码。
js
console.log('start');
setTimeout(()=>{
console.log('setTimeout');
setTimeout(()=>{
console.log('inner');
})
console.log('end');
},1000)
new Promise((resolve,reject) =>{
console.log('promise');
resolve()
})
.then(()=>{
console.log('then1');
}).then(()=>{
console.log('then2');
})
1.代码从上往下执行,首先运行同步代码,将start打印
2.遇到setTimeout,将其存入宏任务队列中
3.promise是同步,所以要先执行,打印promise
4..then是微任务,所以将两个.then依次存入微任务队列
5.没有同步代码可以执行了,于是开始执行微任务队列,依次输出 then1 then2
6 执行宏任务,先将setTimeout打印,此时发现还有一个定时器,于是将其存入宏任务队列
7 执行同步代码打印 end
8 执行宏任务队列中的最后一个宏任务,打印inner
程序执行完毕!
面试官环节
请你写一下以下代码的运行结果:
js
//必考题
console.log('script start')
async function async1() {
await async2()//浏览器给await开小灶,await后面的代码当成同步代码立即调用
console.log('async1 end')
}
async function async2() {
console.log('async2 end')
}
async1()
setTimeout(function () {
console.log('setTimeout')
}, 0)
new Promise(resolve => {
console.log('Promise')
resolve()
})
.then(function () {
console.log('promise1')
})
.then(function () {
console.log('promise2')
})
console.log('script end')
看到这里,你懵了。我的发?这是啥?这是ES7中新增的Promise的进阶版。async就代表Promise,await就是.then。
注意:
await会阻塞后续代码的执行,
将后续代码推入到微任务队列,并且await会先执行,不会进入微任务队列
接着按照上面的步骤执行就好了。
告诉大家结果:
- script start
- async2 end
- Promise
- script end
- async1 end
- promise1
- promise2
- setTimeout
微任务比宏任务先执行?
答案是NO!因为在宏任务中也可能会存在微任务,此时宏任务比微任务先执行,掘友们可千万别踩坑啊!
结尾
拿下这篇文章,就算面试官写的代码再复杂,你都能一眼识破真相!我的发,太简单了!