vlambda博客
学习文章列表

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(4Date.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(时间戳);