JavaScript之Event Loop

JavaScript基于单线程执行模型,在同一时间只能执行一个任务。那么,为什么JavaScript是单线程?

JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?

那么JavaScript是如何通过单线程的机制来实现整个运行机制的呢?


Event Loop

这是整个Event Loop的结构图,在JS部分,heap(堆)处理内存分配,而所有同步任务都在主线程上执行,形成一个stack(执行栈),stack里面存放着当前函数执行的调用链。


举个栗子

function foo() {
  console.log(1);

  window.setTimeout(() => {
    console.log(2);
  }, 0);

  console.log(3);
}

foo();

输出结果自然是1,3,2。再看下一个栗子。

console.log(1);

window.setTimeout(function() {
  console.log(2);
}, 0);

new Promise((resolve) => {
  window.setTimeout(function() {
    console.log(3);
  }, 0);
  console.log(4);
  resolve();
})
.then(() => {
  window.setTimeout(function() {
    console.log(5);
  }, 0);
  console.log(6);
});

console.log(7);

要解释这个问题,需要引入另外一个概念:macrotask & microtask

Macrotasks(宏任务)包含生成dom对象、解析HTML、执行主线程js代码、更改当前URL还有其他的一些事件如页面加载、输入、网络事件和定时器事件。从浏览器的角度来看,macrotask代表一些离散的独立的工作。当执行完一个task后,浏览器可以继续其他的工作如页面重渲染和垃圾回收。

Microtasks(微任务)则是完成一些更新应用程序状态的较小任务,如处理promise的回调和DOM的修改,这些任务在浏览器重渲染前执行。Microtask应该以异步的方式尽快执行,其开销比执行一个新的macrotask要小。Microtasks使得我们可以在UI重渲染之前执行某些任务,从而避免了不必要的UI渲染,这些渲染可能导致显示的应用程序状态不一致。

如下图所示,在一次event loop中,主线程在任务队列中先检查macrotask队列,有的话执行且只执行一个,接着检查microtask队列,有的话执行所有的microtasks,然后再检查是否需要更新渲染。值得注意的是,这一次event loop的执行需要在16ms(1秒钟均匀60帧的画面才能让人感觉流畅)内完成,否则会导致页面卡帧甚至假死。


macrotask & microtask

所以回到上个栗子中,正确的顺序是1,4,7,6,2,3,5。


桥豆麻袋

细心的盆友可能已经发现了,图中不是说会先执行一个macrotask任务,然后再清空microtask队列嘛?为何实际运行时所有的microtasks都在macrotask前面运行了?
这是因为整个代码块(main)开始执行时,所有这些代码在macrotask queue中,主线程将其取出来执行。所以其实是代码块(main)生成的microtasks先执行


划重点

是的,没错,task生成的task也算在当次事件循环中,所以如果你像下面这样,microtask生成了microtask的话:

function foo() {
  console.log("Let's get started");
  bar();
  setTimeout(function() {
    console.log("Where is timeout callback?");
  }, 0);
  console.log("foo done");
}

function bar() {
  return Promise.resolve().then(function() {
    console.log("Promise then");
    return bar();
  });
}

foo();

那么恭喜你离成为一名优秀的高级bug工程师又近了一步。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值