关于前端:一文搞懂javascript事件循环原理前端每日一题v221116

33次阅读

共计 2374 个字符,预计需要花费 6 分钟才能阅读完成。

一文搞懂 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');

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

正文完
 0