likes
comments
collection
share

理解js运行机制

作者站长头像
站长
· 阅读数 38

理解js运行机制

你是否经常会在各类面试题中看到类似的下面这种代码输出题,而又经常感到困惑呢? 没关系,这一次让我们一起来理解消化掉他。

console.log('script start');

new Promist(function(resolve) {
  console.log('1');
  resolve();
}).then(functionn() {
  console.log('2');
})

setTimeout(function() {
  console.log('3');
},0)
console.log('script end');

关于js

众所周知,Javascript是一门单线程的语言。但是如果遇到了setTimeout这样的代码,傻傻的等着岂不是显得我们比较呆呢,这时就引出了今天的主角事件循环(也就是Event Loop);

关于Event Loop

我们先通过一个demo来看:

console.log('1');
setTimeout(function() {
  console.log('2');
},1000)
console.log('3');

对于初识js的同学来说,可能和我刚认识js的时候一样,理所应当的认为会按照代码顺序输出1,2,3。 而当我们自信的把代码放到控制台之后,会发现结果和我们想象的并不一致。

我们通常会将js区分为两种类型:同步任务和异步任务。上面的setTimeout就是异步的一种,当浏览器遇到了异步任务时,会将他放到任务队列(Task Queen)中,等待主线程中同步任务的执行,也保证了代码不会发生阻塞的情况。

这时我们看上方的demo,首先当执行到setTimeout时,会先将回调的函数放到任务队列中,等到主线程输出3之后,事件队列拿到了任务过后便将任务压入执行栈(stack)当中,执行输出2。

宏任务(Macrotask)和微任务(Microtask)

我们常见的异步任务有:setTimeout, setInterval,promise.then等等。我们再来看一个demo:

console.log('1');
setTimeout(function() {
  console.log('2');
},0)
new Promise(function(resolve) {
  console.log('3');
  resolve();
}).then(function() {
  console.log('4');
})
console.log('5');

根据上面所写:我们比较自信的写下了自己的答案:1,5,2,3,4。然后我们把代码放到控制台一看:嗯,打脸了。 这时就要请出我们今天的另外两位主角宏任务Macrotask微任务Microtask了。

常见的宏任务和微任务有: macrotask: setTimeout, setInterval, setImmediate, I/O, UI rendering microtask: process.nextTick, Promise.then

这里首先解释一下: promise本身属于同步任务,而他的回调函数then,catch,finally才是异步任务。如果对Promise有不懂的地方,可以戳此了解Promise

我们先来看一下上面代码的运行结果:1,3,5,4,2。可以看到Promise的函数代码的异步任务会优先于setTimeout的延时为0的任务先执行。 究其原因,异步任务又被分为宏任务微任务。主线程执行过程中,将setTimeout的回调,放到宏任务队列中,而遇到promise的回调,放到微任务队列中。

事件循环,宏任务,微任务的关系如图所示: 理解js运行机制

在此可以简单总结一下事件循环的流程: 1.首先执行主线程,遇到异步任务,将其添加至对应宏任务微任务队列中。 2.主线程执行结束,查看微任务队列,依次执行。 3.微任务队列清空后,查看宏任务队列,执行排在最前面的一个宏任务,每执行完一个宏任务后,就会查看一次微任务队列,如果有待执行的微任务,就先将微任务队列中的所有微任务执行完成后,再执行下一个宏任务,依次类推。

由此流程可知: 上方demo首先执行主流程输出了1 -> 遇到setTimeout时,将其放入宏任务队列中 -> 遇到promise,输出3,将其回调放入微任务队列中 -> 继续执行输出5 -> 主线程运行结束,检查微任务队列,执行promise的回调then,输出4 -> 微任务队列清空,执行宏任务,输出2 -> end。

关于async和await

大致了解了事件循环的机制后,那么问题来了,基于promise的async和await又是怎样执行的呢? 什么是async和await呢?async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。戳此查看async和await详细解析

再来看一个demo:

console.log(1);
setTimeout(() => {
  console.log(2)
},0)
fn();
async function fn () {
  console.log(3);
  await fn1();
  console.log(4);
}

async function fn1 () {
  console.log(5);
}
console.log(6);

那么在这里,await执行会有两种结果:是 或者 不是 Promise对象

当等待的不是Promise对象时,await会阻塞下面的代码,先执行async外面的同步代码,再执行async函数中之前阻塞的代码。

当等待的是Promise对象时,同样阻塞下面的代码,先执行async外面的同步代码,等着Promise对象状态fulfilled之后,执行之前阻塞的代码。

个人理解:await这里还是一个Promise,执行到await这里时,向微任务队列中添加了一个微任务,此微任务执行await后被阻塞的代码。

所以正确输出应该是:1,3,5,6,4,2。

参考文献

转载自:https://juejin.cn/post/6903763029389737998
评论
请登录