JS面试题:JS运行机制

178 阅读3分钟

1. 关于JavaScript

JavaScript是一门单线程语言,单线程就意味着,所有任务都需要排队,前一个任务结束才会执行下一个任务,如果前面任务耗费的时间很长,那后面的任务就得一直等着,这样很明显会造成资源分配的浪费。

所以JavaScript语言设计者为了规避这个问题,把JavaScript这边所有的任务可以分成两种,分别为同步任务和异步任务

同步任务: 在主线程上排队的任务,前一个任务执行完毕,才能继续执行下一个任务

异步任务: 不进入主线程,是进入任务队列的任务,只有任务队列通知主线程,某个异步任务可以执行了,任务才会进入主线程去执行

2. JavaScript的事件循环(EventLoop)

先执行同步操作,异步操作排在事件队列里

  1. 先判断是同步还是异步任务,同步任务就进入主线程,异步任务就进入Event Table
  2. 异步任务在Event Table中注册事件,当满足触发条件的时候,会被推入到Event Queue
  3. 同步任务进入到主线程中执行,当主线程空闲时,才会去Event Queue中看是否有需要执行的异步任务,如果有,就推入主线程中执行 js执行机制.png

3. 宏任务和微任务是什么

异步任务分为:宏任务和微任务

那么什么是宏任务什么是微任务?

宏任务:setTimeout、setInterval、整体代码script

微任务:Promise().then

执行顺序:先执行微任务然后再执行宏任务

js执行.png

4. 例题

1. 同步任务

同步任务:按顺序一步步执行

    console.log(1)
    console.log(2)
    console.log(3)

输出结果:

// 输出结果:1 2 3

2. 同步和异步

顺序:先同步后异步

    console.log(1)
    setTimeout(() => {
      console.log(4)
    }, 100)
    setTimeout(() => {
      console.log(3)
    }, 0)
    console.log(2)

输出结果:

// 输出结果:1 2 3 4

讲解:

  1. 先执行同步任务,按照顺序一步步来
  2. 然后开始执行异步任务,异步任务开始执行时,会将异步任务放入事件表格(EventTable)中,当满足了某些条件(比如这里的条件就是0ms和100ms)之后,才会从事件表格中注册到事件队列(EventQueue),当第1步中的同步事件完成了,才会从事件队列中获取任务去执行

3. 宏任务和微任务

执行顺序:同步 ==> 异步(微任务 ==> 宏任务)

同步 ==> 微任务 ==> 宏任务

    console.log(1);
    setTimeout(function () {
      console.log(5)
    }, 0);

    new Promise(function (resolve) {
      console.log(2);
      resolve();
    }
    ).then(function () {
      console.log(4)
    });
    console.log(3);

执行结果:

// 执行结果:1 2 3 4 5

讲解: 遇到new Promise直接执行(属于同步任务),而promise().then中是微任务,所以这边先执行同步任务,然后执行异步任务,其中异步任务重微任务先执行,然后再执行宏任务

4. 混合任务题型

顺序:同步 --> 微任务(promise.then) --> 宏任务(setTimeout)

    console.log(1);
    setTimeout(function () {
      console.log(2);
      new Promise(function (resolve) {
        console.log(3);
        resolve();
      }).then(function () {
        console.log(4)
      })
    })
    new Promise(function (resolve) {
      console.log(5);
      resolve();
    }).then(function () {
      console.log(6)
    })

    setTimeout(function () {
      console.log(7);
      new Promise(function (resolve) {
        console.log(8);
        resolve();
      }).then(function () {
        console.log(9)
      })
    })

输出结果:

// 输出结果:1 5 6 2 3 4 7 8 9

讲解:

  1. 首先,先按照顺序执行同步任务:1 5
  2. 同步任务执行完成了,开始执行异步任务,异步任务先微任务后宏任务,所以先执行promise.then中的6
  3. 微任务执行完毕,开始宏任务,第一个setTimeout中输出2 3 4
  4. 接着执行第二个setTimeout中输出7 8 9

5. 总结

  1. 同步先执行,异步后执行
  2. 遇到new Promise直接执行,then中的方法直接放入微任务队列中
  3. 遇到setTimeout放入宏任务队列中
  4. 执行顺序:同步-->微任务(promise.then)-->宏任务(setTimeout)