之前没有特别注意有关JavaScript中的事件循环,导致在一些时候不知道这些代码为什么会这样的问题出现了,特此写了简单的笔记以此来记录,此笔记可能不完善,会随着自己学习的加深而去不断地完善。
一、明确基础概念
- JavaScript是一门单线程语言,意味着,完成一个任务后才会执行下一个任务。
- 事件循环是JavaScript的执行机制。
二、JavaScript事件循环示意图
当一个JavaScript检测到任务时,首先会判断是同步任务还是异步任务,是同步任务就放到执行栈上执行,异步任务则放到异步队列里面(队列先进先出原则,懂数据结构应该就明白,异步任务还分为微任务和宏任务,这是后面要将的),当执行栈任务完成后,会去看异步队列是否有可以执行的任务,有就执行,不断重复该过程就是JavaScript的事件循环机制。
三、宏任务与微任务
宏任务:
setTimeout, setInterval, setImmediate, requestAnimationFrame, I/O, UI rendering,script标签的整体代码,Ajax
微任务:
Promise的then/catch/finally,MutaionObserver,process.nextTick(Node.js)
执行顺序: 由于第一次进入,我们会执行script标签里面的代码,相当于进来就是一个宏任务了,在这个宏任务中,有些代码是同步,有些是异步,我们从上往下开始执行,遇到宏任务和微任务就放到异步队列中,等到主线程执行栈中的同步任务全部完成后,去异步队列中找微任务执行,将所有可执行的微任务完成后再执行宏任务,然后不断重复。
下面来一道题目检测一下,看下自己是否理解
题目一
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的状态没有改变。