js事件循环机制(Event Loop)
js事件循环机制(Event Loop)
javascript从诞生之日起就是一门 单线程的 非阻塞的
脚本语言,单线程
意味着,javascript代码在执行的任何时候,都只有一个主线程来处理所有的任务,非阻塞
靠的就是 event loop(事件循环),本文就讲解下事件循环。
event loop它最主要是分三部分:主线程、宏队列(macrotask)、微队列(microtask)
js的任务队列分为同步任务和异步任务,所有的同步任务都是在主线程里执行的,异步任务可能会在macrotask或者microtask里面
主线程
就是访问到的script标签里面包含的内容,或者是直接访问某一个js文件的时候,里面的可以在当前作用域直接执行的所有内容
宏队列(macrotask)
setTimeout、setInterval、setImmediate(Node.js 环境)、I/O、UI rendering
微队列(microtask)
promise.then、process.nextTick(Node.js 环境)
执行顺序
1、先执行主线程
2、遇到宏队列(macrotask)放到宏队列(macrotask)
3、遇到微队列(microtask)放到微队列(microtask)
4、主线程执行完毕
5、执行微队列(microtask),微队列(microtask)执行完毕
6、执行一次宏队列(macrotask)中的一个任务,执行完毕
7、执行微队列(microtask),执行完毕
8、依次循环…
下面代码执行结果:
console.log(1); // 1
setTimeout(function() { //2
console.log(2);
}, 0);
new Promise(function(resolve) {//3
console.log(3);
resolve(Date.now());
}).then(function() {
console.log(4);
});
console.log(5); //4
setTimeout(function() { // 5
new Promise(function(resolve) {
console.log(6);
resolve(Date.now());
}).then(function() {
console.log(7);
});
}, 0);
执行顺序
1.0 主线程 碰到1: 打印1
2.0 碰到2:打印3,把promise.then放到宏任务里面 碰到3: 放到微任务里面 碰到4: 打印5 碰到5: 放到宏任务里面
3.0 执行微任务3:打印4
4.0 执行宏任务2(因为先进去的):打印2
5.0 执行宏任务5,打印6 里面有promise.then 放到微任务 宏任务执行完毕
6.0 执行微任务:打印7
最终结果是:1 3 5 4 2 6 7
我们在Promise.then实现一个稍微耗时的操作,
console.log(1); //1
var start = Date.now();
setTimeout(function() { // 2
console.log(2);
}, 0);
setTimeout(function() { //3
console.log(4, Date.now() - start);
}, 400);
Promise.resolve().then(function() { //4
var sum = function(a, b) {
return Number(a) + Number(b);
}
var res = [];
for(var i=0; i<5000000; i++) {
var a = Math.floor(Math.random()*100);
var b = Math.floor(Math.random()*200);
res.push(sum(a, b));
}
res = res.sort();
console.log(3);
})
1.0 主线程 碰到1: 打印1
2.0 碰到2,3:放到宏任务 碰到4:放到微任务里
3.0 执行微任务4:打印3
4.0 执行宏任务2,3:2 4 3986
本来要设定的是400ms后输出,但因为之前的任务耗时严重,导致之后的任务只能延迟往后排。说明,setTimeout和setInterval这种操作的延时是不准确的,这两个方法只能大概将任务400ms之后的宏任务中,但具体的执行时间,还是要看线程是否空闲。若前一个任务中有耗时的操作,或者有无限的微任务加入进来时,则会阻塞下一个任务的执行。
async-await
那么 async-await 的代码怎么执行呢
function A() {
return Promise.resolve(Date.now());
}
async function B() {
console.log(Math.random());
let now = await A();
console.log(now);
}
console.log(1);
B();
console.log(2);
其实,async-await 只是 Promise+generator 的一种语法糖而已。上面的代码我们改写为这样,可以更加清晰一点:
function B() {
console.log(Math.random());
A().then(function(now) {
console.log(now);
})
}
console.log(1);
B();
console.log(2);
输出结果是:1, 0.4793526730678652(随机数), 2, 1557830834679(时间戳);