js是一门单线程的非阻塞的脚本语言
单线程意味着,js在执行的时候,只有一个主线程来处理所有的任务。
非阻塞则是当代码需要进行一项异步任务(如I/O事件、网络请求)的时候,主线程会挂起(pending)这个任务,然后在异步任务返回结果后,将注册的回调函数放入任务队列中,等待主线程空闲的时候(调用栈被清空),再根据一定的规则读取任务队列中的任务。
任务队列分为两种:宏任务(macro task)和微任务(micro task)。
宏任务和微任务
宏任务,macrotask,也叫tasks。
包括:script全部代码、setTimeout、setInterval、requestAnimationFrame (浏览器独有)、I/O、UI rendering (浏览器独有)
微任务,microtask,也叫jobs。
包括:Promise、ajax、Object.observe(废弃)、MutationObserver
事件执行顺序(也可以直接看下面的简单版本):
- 执行全局的同步代码,全局Script代码执行完毕后,调用栈Stack会清空;
- 依次从微队列microtask queue中取出位于队首的回调任务,放入调用栈Stack中执行,直到直到把microtask queue中的所有任务都执行完毕。(如果在执行microtask的过程中,又产生了microtask,那么会加入到队列的末尾,也会在这个周期被调用执行) 。microtask queue中的所有任务都执行完毕,此时microtask queue为空队列,调用栈Stack也为空;
3.如果需要的话,渲染页面;
4.取出宏队列macrotask queue中位于队首的任务,放入Stack中执行;执行完毕后,调用栈Stack为空;
重复第2-4步骤;
......
简单的来说,事件循环是指: 先执行一次宏任务,然后清空微任务列表,然后需要的话,渲染页面,然后循环上面的步骤。如下图

练习题目
console.log(1);
setTimeout(() => {
console.log(2);
Promise.resolve().then(() => {
console.log(3)
});
});
new Promise((resolve, reject) => {
console.log(4)
resolve(5)
}).then((data) => {
console.log(data);
}).then((data) => {
console.log('another');
})
setTimeout(() => {
console.log(6);
})
console.log(7);
正确答案
1
4
7
5
another
2
3
6
解析:
首先1 4 7属于全局的同步代码,先执行。注意new Promise的时候也属于同步执行。
然后再执行微任务队列中所有的任务,即5, 此时又向微任务队列中添加了一个任务,一并在此周期内被调用执行,即 another
然后要取出一个宏任务队列中的任务,即2
然后再执行微任务队列中所有的任务, 即3
然后要取出一个宏任务队列中的任务,即6
网友评论