事件循环概述
事件循环是用来实现异步特性的。
事件循环中的几个概念:
主线程:
- 理解为同步任务的先进先出,一旦调用,同步任务就执行。
执行栈:
- 先入后出的数据结构,一个任务来到栈底就立即执行,然后释放。
任务队列:
- 包括宏任务队列和微任务队列,当执行栈空的时候,就会从任务队列中,取任务来执行。
宏任务和微任务:
- 异步任务,通常在主线程执行完同步任务完成之后,才会来执行宏任务和微任务。微任务的优先级要比宏任务高。
常见的宏任务:
- script(全局任务)、setTimeout、setInterval、setImmediate、I/O、UI Rendering。
常见的微任务:
- Process.nextTick、Promise、Object.observer、MutationObserver、queueMicrotask(将函数添加到微任务队)
注意:new Promise的过程属于同步任务,resove或者reject之后才算微任务。
执行过程
主线程首先执行完同步任务,然后会去任务队列中执行宏任务,如果在执行宏任务的过程中发现有微任务,这时候微任务比宏任务先执行。全部执行完成之后等待主线程调用,调用完成之后再去任务队列中查看是否还有异步任务有待执行,循环往复。
事件循环详解
在实际开发中,往往是同步代码和异步代码都有。在js执行时,还是从第一行代码开始执行,遇到函数就将其添加到栈中,然后执行同步操作;如果遇到异步函数,则根据其类型,宏任务就添加到宏任务队列,微任务添加到微任务队列。直到同步代码执行完毕,则开始执行异步操作。
异步操作后于同步操作,异步操作内部也是分先后顺序的。总的来说:
- 微任务先于宏任务执行
- 微任务与微任务之间根据先后顺序执行,宏任务与宏任务之间根据延迟时间顺序执行
- 微任务在下一轮DOM渲染前执行,宏任务在下一轮DOM渲染之后执行
- 每个任务的执行都是一次出栈操作,直到栈被清空
微任务在下一轮DOM渲染前执行,宏任务在之后执行
let div = document.createElement('div');
div.innerHTML = 'hello world';
document.body.appendChild(div);
let list = document.getElementsByTagName('div');
console.log('同步任务 length ---', list.length);
console.log('start');
setTimeout(() => {
console.log('setTimeout length ---', list.length);
alert('宏任务 setTimeout 阻塞'); // 使用alert阻塞js执行
}, 1000);
Promise.resolve().then(() => {
console.log('promise then length ---', list.length);
alert('微任务 promise then 阻塞');
});
console.log('end');
// 运行结果:
// 同步任务 length --- 1
// start
// end
// promise then length --- 1
// setTimeout length --- 1
// alert('微任务 promise then 阻塞'),此时的页面是白屏,因此处于DOM渲染前
// alert('宏任务 setTimeout 阻塞'),此时页面出现了hello world,因此处于DOM渲染后
微任务中创建宏任务
new Promise((resolve) => {
console.log('promise 1');
setTimeout(() => {
console.log('setTimeout 1');
}, 500);
resolve();
}).then(() => {
console.log('promise then');
setTimeout(() => {
console.log('setTimeout 2');
}, 0);
});
new Promise((resolve) => {
console.log('promise 2');
resolve();
});
// 运行结果:
// promise 1
// promise 2
// promise then
// setTimeout2
// setTimeout 1
解析:
js执行代码,遇到两个Promise,则分别添加到微任务队列,同步代码执行完毕。
在微任务队列中根据先进先出,第一个Promise先执行,遇到setTimeout,则添加到宏任务队列,resolve()返回执行结果并执行then,事件循环将其继续添加到微任务队列;第一个Promise执行完毕,执行第二个Promise。
继续执行微任务队列,直到清空队列。遇到setTimeout,并将其添加到宏任务队列。
宏任务队列现在有两个任务待执行,由于第二个setTimeout的延迟事件更小,则优先执行第二个;如果相等,则按照顺序执行。
继续执行宏任务队列,直到清空队列。
宏任务中创建微任务
setTimeout(() => {
console.log('setTimeout 1');
new Promise((resolve) => {
console.log('promise 1');
resolve();
}).then(() => {
console.log('promise then');
})
}, 500);
setTimeout(() => {
console.log('setTimeout 2');
new Promise((resolve) => {
console.log('promise 2');
resolve();
})
}, 0);
// 运行结果:
// setTimeout2
// promise2
// setTimeout1
// promise1
// promise then
解析:
js执行代码,遇到两个setTimeout,将其添加到宏任务队列,同步代码执行完毕。
先检查微任务队列中是否有待处理的,刚开始肯定没有,因此直接执行宏任务队列中的任务。第二个为零延迟,需要优先执行。遇到Promise,将其添加到微任务队列。第一个宏任务执行完毕。
在执行第二个宏任务时,微任务队列中已经存在待处理的,因此需要先执行微任务。
微任务执行完毕,并且延迟时间到期,第一个setTimeout开始执行。遇到Promise,将其添加到微任务队列中。
执行微任务队列中的Promise,执行完毕后遇到then,则将其继续添加到微任务队列。
直到所有微任务执行完毕。
宏任务中创建宏任务
setTimeout(() => {
console.log('setTimeout 1');
setTimeout(() => {
console.log('setTimeout 2');
}, 500);
setTimeout(() => {
console.log('setTimeout 3');
}, 500);
setTimeout(() => {
console.log('setTimeout 4');
}, 100);
}, 0);
// 运行结果
// setTimeout1
// setTimeout4
// setTimeout2
// setTimeout3
无需解析。
微任务中创建微任务
new Promise((resolve) => {
console.log('promise 1');
new Promise((resolve) => {
console.log('promise 2');
resolve();
});
new Promise((resolve) => {
console.log('promise 3');
resolve();
})
resolve();
})
// 运行结果:
// promise1
// promise2
// promise3
无需解析。
标签:执行,console,log,队列,JS,任务,setTimeout,event,loop From: https://www.cnblogs.com/luckest/p/16656933.html