Event Loop分为浏览器需要区分浏览器环境和Node环境。我目前关心点在于浏览器环境。
所谓事件环即:主线程循环不停的从任务队列中读取任务。
基础概念:
1. javascript是主线程(用于用户交互和操作DOM)为单线程,可以有多个受控于主线程的子线程用于计算等(不能操作DOM)。
2. 主线程中的任务是同步任务,同步任务位于执行栈中,按照顺序执行,执行栈清空后才去任务队列读取任务。
3. 异步任务主要用于解决I/O设备或者计算速度较慢的问题。属于子线程。
4. 任务队列中全部为异步任务(回调函数),微任务,宏任务。
微任务: promise的then()方法
宏任务:
- setTimeout,setInterval
- setImmediate(只兼容IE,默认低于setTimeout(0))
- MutationObserver(性能有问题被弃用)
- MessageChannel(高版本浏览器)
执行顺序(同步任务执行完后)
1. 只有异步任务时。
异步任务来源主要有I/O设备,用户交互和Network事件,如键盘键入,鼠标单击和页面滚 动等触发事件。
遇到异步任务时异步任务先被挂起,等回调函数拿到返回结果后才进入任务队列等待被主线 程调用。
等到执行栈中的同步任务执行完成后,主线程才到任务队列中读取异步任务。任务队列中的 任务执行顺序依照“先进先出”。主线程依次从任务队列中读取任务。
2. 只有定时任务时。
根据时间排序,当时间到达后,把对应回调放到队列。
setTimeout(() => {
console.log(1);
setTimeout(() => {
console.log(4);
}, 1000);
}, 1000);
setTimeout(() => {
console.log(2);
}, 2000);
setTimeout(() => {
console.log(3);
}, 3000);
执行顺序为1,2,4,33. 当任务队列中同时有微任务和宏任务时。(微任务先于宏任务)
- 执行微任务,先把微任务清空
- 执行一个宏任务
- 再去执行微任务,清空
- 执行一个宏任务(依次不停循环)
setTimeout(() => { console.log('setTimeout1') Promise.resolve().then(data => { console.log('微任务1') }) }, 0); Promise.resolve().then(data=>{ console.log('微任务2') setTimeout(() => { console.log('setTimeout2') }, 0); });
执行结果是: 微任务2,setTimeout1,微任务1,setTimeout2.