一文搞懂javascript事件循环原理?「前端每日一题v22.11.16」

理解javascript的第一步,就是要理解事件循环机制。然而要真正的理解javascript的事件循环机制并不容易,因为它是javascript引擎最根底的局部。它能够让单线程的javascript以非阻塞形式执行

事件循环机制Event Loop,要真正理解这些,咱们可能须要晓得javascript引擎相干内容,比方调用栈,宏工作,微工作,工作队列等相干,一起看一下这些概念

调用栈

调用栈是一种跟踪javascript代码执行的数据结构,它是一个栈,因而遵循先进后出的数据结构,执行的每个函数都示意为调用栈中的一个帧,并放在前一个函数的顶部

举个例子,这是最常见的一段javascript代码

function foo(){  console.log('foo')  bar()}function bar(){  console.log('bar')}foo()

咱们具体的讲述一下上述代码的执行流程

  1. 调用栈最后是空的,疏忽后面所有的函数,直到foo被调用
  2. 函数foo被压入调用栈,调用栈列表为foo
  3. 执行foo中的代码console.log,栈列表增加console.log函数,这个函数执行完就会立即被推出栈,所以栈列表还是foo
  4. 代码执行到bar时候,该函数被调用
  5. bar函数被压入调用栈,调用栈列表为bar-foo
  6. 执行bar中的代码,直到全副执行结束,调用栈列表没变
  7. 返回来继续执行foo函数中bar之后的代码
  8. 删除调用栈列表中bar函数,调用栈列表为foo
  9. foo中的bar函数后续没有可执行代码
  10. 删除调用栈列表中的foo,此时调用栈列表为空

一开始咱们会往空的调用栈中推入函数,执行完函数代码之后,调用栈又会移除这个函数,最终又会失去一个空的调用栈,如果在执行过程中,

工作和工作队列

下面的案例次要是针对javascript单线程的执行形式,然而这种形式是不可取的,比方如果咱们碰到了定时器,或者申请数据等执行工夫比拟长的代码的话,前面的逻辑就得等这些代码执行实现能力进行下一步

所以就有了工作队列的概念

工作分为同步工作和异步工作,区别就是同步工作执行后立即就能取到后果,异步工作须要等一会儿能力取到后果

同步工作执行依照调用栈执行逻辑,执行实现移出调用栈,异步工作执行也会进入调用栈,不同的是它会将其回调函数或者回调工作放入一个工作队列,工作队列遵循先进先出的准则,放入工作队列的函数不会立即执行,须要期待调用栈中同步的工作执行实现

当调用栈清空之后,也就是所有同步工作完结之后,解释器开始读取工作队列执行,将曾经实现的异步工作放入调用栈执行

看代码

console.log('foo')setTimeout(() => {  console.log('bar')}, 1000)console.log('loo')
  1. console.log('foo')压入调用栈,执行实现推出调用栈
  2. setTimeout压入调用栈
  3. 浏览器的定时线程会等工夫完结后将setTimeout中的回调函数箭头函数放入工作队列
  4. setTimeout推出调用栈
  5. console.log('loo')压入调用栈,执行并推出
  6. 此时同步工作全副执行实现,调用栈为空
  7. 1s过后,浏览器的定时线程将匿名函数放入工作队列
  8. 调用栈为空,执行工作队列匿名函数内容
  9. console.log('bar')执行
  10. 匿名函数推出调用栈
  11. 调用栈为空

宏工作和微工作

后面说到的工作队列,其实还分为宏工作和微工作,这两种都属于异步工作,它们的区别就在于它们的执行程序。

为什么会辨别宏工作和微工作,最次要的一点就是如果工作队列中某一个工作须要1个小时,后续的工作都须要期待一个小时,这显然是不合理的,所以宏工作和微工作最次要的一个目标就是辨别工作的优先级,微工作优先级高于宏工作

宏工作

  • 整体代码都属于宏工作
  • UI交互事件
  • I/O
  • setTimeout
  • setInterval
  • setImmediate
  • requestAnimationFrame

微工作

  • process.nextTick
  • MutationObserver
  • Promise.then catch finally
console.log('foo')setTimeout(() => {  console.log('bar')}, 0)new Promise((res) => {res(1)}).then((d) => {console.log(d)})

程序为foo、1、bar

事件循环

下面的内容联合起来,基本上就是整个事件循环的机制了

  1. 先从调用栈开始
  2. 同步工作压入调用栈,开始执行
  3. 碰到异步工作,将有后果的回调压入工作队列,留神辨别宏工作和微工作
  4. 调用栈清空之后,微工作队列依照先入先执行程序开始执行
  5. 微工作队列清空之后,一个循环完结
  6. 进入宏工作队列直到宏工作队列清空
async function async1() {    console.log('async1 start');    await async2();    console.log('async1 end');}async function async2() {    console.log('async2');}console.log('script start');setTimeout(function () {    console.log('setTimeout');}, 0)async1();new Promise(function (resolve) {    console.log('promise1');    resolve();    console.log('promise2')}).then(function () {    console.log('promise3');});console.log('script end');

依照以上逻辑,做完这个题就能够出师了,当前碰到所有的这种执行程序的都不怕了