js引擎的执行机制
前提
-
通过该笔记,在有异步代码同时出现时能够判断出js的执行顺序
-
js是单线程语言
-
js的Event loop是js的执行机制
js单线程的原因
如果js是多线程的话,对于一个dom元素,如果一个线程删除掉这个元素,另一个线程要修改这个元素,这是就会出现矛盾
js需要异步的原因
js是单线程的,单线程在执行程序时,所走的程序路径按照连续顺序拍下来,前面的处理好才会执行后面的代码,即自上而下执行,若js没有异步的话,当前面的代码执行需要花费很多时间而与后面的代码没有关联,后面的代码就会被堵塞,影响用户体验
js异步的实现是通过事件循环来实现的
js中的event loop
js中,代码会被划分成宏任务 (macro-task) 和微任务 (micro-task),其中,宏任务包括: 整体代码script, setTimeout, setInterval; 微任务包括: Promise, process.nextTick
js的执行机制
- 执行一个宏任务,在这个过程中如果遇到微任务,就将其放到微任务的事件队列中,当遇到宏任务时,则会把宏任务放在宏任务的事件队列
- 当当前的宏任务执行结束时,就会查看微任务的事件队列,并将里面的所有微任务(队列,则是先进入队列的微任务先执行,后进入的后执行)依次执行完,再执行下一个宏任务
- 如果在执行microtask微任务的过程中,又产生了microtask,那么会加入到队列的末尾,也会在这个周期被调用执行,即在一个宏任务执行的过程中遇到的微任务都会在这个宏任务周期内执行,微任务会按照队列顺序执行
- 在执行的过程中,如果遇到同步代码,则会先执行,即使是像for循环这种需要花费时间的
代码示例:
setTimeout(function(){
console.log('定时器开始啦')
});
new Promise(function(resolve){
console.log('马上执行for循环啦');
for(var i = 0; i < 10000; i++){
i == 88 && resolve();
}
}).then(function(){
console.log('执行then函数啦')
});
console.log('代码执行结束');
首先,js先执行script所有代码,在遇到同步代码时会先执行,异步代码则会区分宏任务和微任务。看到setTimeout时就会把它放在宏任务,再往下执行,由于promise里面的代码是同步的即先回输出 ’马上执行for循环啦’,再执行for循环,当i等于88时,会遇到resolve这个微任务,则会把它放在微任务的事件队列中,再执行for循环,接着又执行同步代码,输出‘代码执行结束’, 此次宏任务执行完毕,再执行此次留下来的微任务队列,输出‘执行then函数啦’, 当微任务队列执行完后会再执行下一个宏任务队列,输出‘定时器开始啦’
定时器函数
setTimeout(function(){
console.log('执行了')
},3000)
上面的代码,我们一般说3秒后会执行回调函数里面的代码,但这种说法并不严谨,要记住js是单线程,如果3秒后js还在执行其他的代码,就不会执行定时器里的回调函数,得等到主线程空闲的时候才会执行,也有可能10秒后执行
练习
console.log('head');
// 宏任务
setTimeout(() => {
console.log("setTimeout1");
new Promise((resolve) => {
console.log('promise2');
resolve()
}).then(() => {
console.log('then2');
for(let i = 0; i < 100000000; i++) {
if(i == 1000000) console.log(11);
}
})
})
// then是微任务
new Promise((resolve, reject) => {
console.log('promise');
for(let i = 0; i < 100; i++) {
if(i == 90) console.log('ii');
i == 88 && resolve()
}
}).then(() => {
console.log('then1');
new Promise((resolve) => {
console.log('promise');
resolve()
}).then(() => {
console.log('then3');
})
setTimeout(() => {
console.log('settimeout3');
},10)
})
setTimeout(() => {
console.log("setTimeout2");
})
console.log('footer');