Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaScript 的运行机制 #12

Open
yangrenmu opened this issue Jun 23, 2019 · 0 comments
Open

JavaScript 的运行机制 #12

yangrenmu opened this issue Jun 23, 2019 · 0 comments

Comments

@yangrenmu
Copy link
Owner

yangrenmu commented Jun 23, 2019

前言

作为一个前端er,最基本的要求也得知道js的运行机制叭。由于js是单线程的,所以js是按顺序执行的。

console.log('1')
console.log('2')
输出:1,2。果然是按顺序执行的。本文结束。

来点复杂的。

    setTimeout(function () {
      console.log('1')
    })
    new Promise((resolve) => {
      console.log('2')
      resolve()
    }).then(() => {
      console.log('3')
    })
    console.log('4')
输出:2,4,3,1。这个是为啥子嘞。

从 js 单线程说起

js是一门单线程的语言,设计之初,是在浏览器中运行的,方便处理DOM树的。如果是多线程的话,一个线程删除DOM,一个线程修改DOM,这样就会出现问题。HTML5引入了web workers,使js能够创建多个线程,是为了防止大量的数据计算阻塞UI渲染,但这些线程都是受主线程控制的,所以js在本质上还是单线程的。

表面很难的event loop

js 是单线程的,所以一次只能执行一个任务,当一个任务需要很长时间时,主线程一直等待任务的执行完成,在执行下个任务是很浪费资源的。
比如,我们吃泡面时候,先烧水,在等待水烧开的时候,我们可以把泡面打开,作料放好,等水开了,直接泡就好。那个吃干脆面的走开。
所以,js中任务就被分成两种,一种是同步任务,一种是异步任务。执行步骤如下图所示:

  • 1、同步任务都在主线程上执行,形成一个执行栈。
  • 2、异步任务会被放入主线程之外的任务队列中。
  • 3、主线程同步任务执行完后,会去任务队列中查找异步任务,将其放到执行栈中执行。
  • 4、主线程不断重复上面三步,这也就是常说的事件循环。

宏任务和微任务

为了更精细的区分任务,js中可以将异步任务划分为宏任务和微任务。

  • 1、js代码在执行时,首先执行js同步任务,异步任务会放入任务队列中。
  • 2、同步任务执行完后,会去查找有没有微任务可以执行,然后执行所有微任务。第一次事件循环结束。
  • 3、然后进入第二次事件循环,此时,任务队列中的第一个宏任务,将变成同步任务,被首先执行。
  • 4、不断重复1、2、3,以此循环下去,直到所有任务全部执行完成。
常见的宏任务:

setTimeoutsetIntervalMessageChannelpostMessagesetImmediate等等。

常见的微任务:

promise.thenMutationObsever等等。
看下这段代码:

    setTimeout(function () {
      console.log('1')
    })
    new Promise((resolve) => {
      console.log('2')
      resolve()
    }).then(() => {
      console.log('3')
    })
    console.log('4')
    
  • 首先执行setTimeout,进入任务队列,注册成宏任务。
  • Promise,直接执行,输出:2,然后promise.then,进入任务队列,注册成微任务。
  • 然后遇到console.log('4'),直接输出:4。
  • 同步任务执行完,从任务队列中找出要执行的微任务,输出:3,第一次事件循环结束。
  • 第二次事件循环从宏任务开始,输出:1。
    下面我们看个更复杂的例子:
    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')
      })
    })
    console.log('10')
  • 首先输出:1,然后注册宏任务setTimeout1。然后输出5,注册微任务promise1,然后注册宏任务setTimeout2,然后输出10。注册了微任务promise1,宏任务setTimeout1、setTimeout2
  • 同步任务执行完后,我们发现有微任务promise1在任务队列中,所以微任务promise1进入主线程,执行微任务promise1,输出:6,第一次事件循环结束,输出了1 5 10 6
  • 随后宏任务setTimeout1进入主线程,执行宏任务setTimeout1,输出:2 3,注册微任务promise2
  • 宏任务setTimeout1执行完后,我们发现有微任务promise2在任务队列中,所以微任务promise2进入主线程,执行微任务promise2,输出:4
  • 随后宏任务setTimeout2进入主线程,输出:7 8,注册微任务promise3
  • 最后微任务promise3进入主线程,输出:9
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant