初学JavaScript事件循环

103 阅读3分钟

之前没有特别注意有关JavaScript中的事件循环,导致在一些时候不知道这些代码为什么会这样的问题出现了,特此写了简单的笔记以此来记录,此笔记可能不完善,会随着自己学习的加深而去不断地完善。

一、明确基础概念

  • JavaScript是一门单线程语言,意味着,完成一个任务后才会执行下一个任务。
  • 事件循环是JavaScript的执行机制。

二、JavaScript事件循环示意图

image.png 当一个JavaScript检测到任务时,首先会判断是同步任务还是异步任务,是同步任务就放到执行栈上执行,异步任务则放到异步队列里面(队列先进先出原则,懂数据结构应该就明白,异步任务还分为微任务和宏任务,这是后面要将的),当执行栈任务完成后,会去看异步队列是否有可以执行的任务,有就执行,不断重复该过程就是JavaScript的事件循环机制。

三、宏任务与微任务

宏任务:

setTimeout, setInterval, setImmediate, requestAnimationFrame, I/O, UI rendering,script标签的整体代码,Ajax

微任务:

Promise的then/catch/finallyMutaionObserver,process.nextTickNode.js

执行顺序: 由于第一次进入,我们会执行script标签里面的代码,相当于进来就是一个宏任务了,在这个宏任务中,有些代码是同步,有些是异步,我们从上往下开始执行,遇到宏任务和微任务就放到异步队列中,等到主线程执行栈中的同步任务全部完成后,去异步队列中找微任务执行,将所有可执行的微任务完成后再执行宏任务,然后不断重复。
image.png

下面来一道题目检测一下,看下自己是否理解

题目一

async function async1() {
    console.log('async1 start');
    await async2();
    console.log('async1 end');
}
async function async2() {
    console.log('async2');
}
console.log('script start');
setTimeout(function () {
    console.log('setTimeout');
}, 0)
async1();
new Promise(function (resolve) {
    console.log('promise1');
    resolve();
    console.log('promise2')
}).then(function () {
    console.log('promise3');
});
console.log('script end');

答案:

script start
async1 start
async2
promise1
promise2
script end
async1 end
promise3
setTimeout

一开始我的答案不是这个,发现自己做错了,又开始怀疑自己,后面才发现,是自己对async和await不了解 await命令后面是一个 Promise 对象

分析过程:

  • 代码一进入,由于前两个是函数声明,不用管他,所以首先输出console.log('script start');,之后遇到了setTimeout,将其放到宏任务队列里面,
  • 接下来继续,遇到async1则输出console.log('async1 start');,之后又遇到了await async2( );这就是我犯错误的地方,我把它以及后面部分全部放到了微任务队列里面,这是不对的,await返回的是一个Promise对象,所以应该运行console.log('async2');,而await async2( );后面的内容则放到微任务队列,
  • 继续执行,输出console.log('promise1'); 和 console.log('promise2'); ,之后将then的内容放到微任务队列中,再输出console.log('script end');
  • 到现在宏任务队列里面有一个setTimeout,微任务队列里面有两个先后分别是console.log('async1 end'); 和 console.log('promise3')
  • 同步任务已经执行完毕了,接下来执行微任务,先后输出console.log('async1 end'); 和 console.log('promise3'),最后取出宏任务输出console.log('setTimeout');
    可能有人和我一样,对await async2( );以及之后的代码,有所疑问,请看如下代码:
async function async1() {
    console.log('async1 start');
    new Promise(function (resolve, reject) {
        console.log('async2');
    }).catch(function (res) {
       console.log('async1 end'); 
    })
    
}
console.log('script start');
setTimeout(function () {
    console.log('setTimeout');
}, 0)
async1();
new Promise(function (resolve) {
    console.log('promise1');
    resolve();
    console.log('promise2')
}).then(function () {
    console.log('promise3');
});
console.log('script end');

我们将await async2();,翻译成了promise这下就一目了然吧(补充说明:有些时候遇到有Promise的问题,请注意是否有reslove和reject),这个问题会导致有些代码不执行,毕竟Promise的状态没有改变。