事件循环(Event Loop)

前言

Event Loop 是 JavaScript 异步编程的核心思想,也是前端进阶必须跨越的一关。特别是在 Promise 出现之后,各种各样的面试题层出不穷,花样百出。这篇文章从现实生活中的例子入手,让你彻底理解 Event Loop 的原理和机制

一、为什么 JavaScript 是单线程的?

JavaScript 是一门 单线程 语言,也就是说同一时间只能做一件事。这是因为 JavaScript 生来作为浏览器脚本语言,主要用来处理与用户的交互、网络以及操作 DOM。这就决定了它只能是单线程的,否则会带来很复杂的同步问题。

举个栗子:假设 JavaScript 有两个线程,一个线程在某个 DOM 节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准呢?
既然 Javascript 是单线程的,它就像是只有一个窗口的银行,客户不得不排队一个一个的等待办理。同理 JavaScript 的任务也要一个接一个的执行,如果某个任务(比如加载高清图片)是个耗时任务,那浏览器岂不得一直卡着?为了防止主线程的阻塞,JavaScript 有了 同步 和 异步 的概念。

在这里插入图片描述

二、什么是事件循环?

在异步代码完成后仍有可能要在一旁等待,因为此时程序可能在做其他的事情,等到程序空闲下来才有时间去看哪些异步已经完成了。所以 JavaScript 有一套机制去处理同步和异步操作,那就是事件循环 (Event Loop)。

书面来说:

  • 所有同步任务都在主线程上执行,形成一个执行栈
  • 而异步任务会被放置到 Task Table,也就是上图中的异步处理模块,当异步任务有了运行结果,就将该函数移入任务队列。
  • 一旦执行栈中的所有同步任务执行完毕,引擎就会读取任务队列,然后将任务队列中的第一个任务压入执行栈中运行。
  • 总结:主线程不断重复第三步,也就是 只要主线程空了,就会去读取任务队列,该过程不断重复,这就是所谓的 事件循环。

    三、宏任务和微任务

    任务队列其实不止一种,根据任务种类的不同,可以分为微任务(micro task)队列和宏任务(macro task)队列。宏任务会进入一个队列,而微任务会进入到另一个不同的队列,且微任务要 优于 \color{red}{优于} 优于 宏任务执行。
    常见的宏任务和微任务:
  • 宏任务:script(整体代码)、setTimeout、setInterval、I/O、事件、postMessage、 MessageChannel、setImmediate (Node.js)
  • 微任务:Promise.then、 MutaionObserver、process.nextTick (Node.js)
  • 四、小试牛刀

    代码如下(示例):

    console.log(1)
     
    setTimeout(function() {
      console.log(2)
    }, 0)
     
    const p = new Promise((resolve, reject) => {
      resolve(1000)
    })
    p.then(data => {
      console.log(data)
    })
     
    console.log(3)
    

    第一个题介绍简单解析一下:

    1. nsole.log(1) 是同步任务, 所以立即执行
    2. setTimeout是宏任务, 所以等微任务做完在执行. (代码 ---> 宏任务队列)
    3. promise.then是微任务, 所以等主线程执行完毕在执行. (代码 ---> 微任务队列)
    4. console.log(3) 是同步任务, 所以立即执行. (代码 ---> 执行栈 ---> 输出区域)
    5. 现在主线程上面的任务全部执行完毕, 然后执行微任务队列的任务, 输出console.log(data)
    6. 现在主线程上面没有任务, 微任务队列也没有任务, 这才这些宏任务队列, 输出console.log(2)

    代码如下(示例):

    
    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();
    }).then(function() {
      console.log('promise2');
    });
    console.log('script end');
    
    

    代码如下(示例):

    
    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)
    
    

    代码如下(示例):

    
      new Promise((resolve, reject) => {
        resolve(1)
     
        new Promise((resolve, reject) => {
          resolve(2)
        }).then(data => {
          console.log(data)
        })
     
      }).then(data => {
        console.log(data)
      })
     
      console.log(3)
    
       console.log(1);
        async function fnOne() {
          console.log(2);
          await fnTwo(); // 右结合先执行右侧的代码, 然后等待
          console.log(3);
        }
        async function fnTwo() {
          console.log(4);
        }
        fnOne();
        setTimeout(() => {
          console.log(5);
        }, 2000);
        let p = new Promise((resolve, reject) => { // new Promise()里的函数体会马上执行所有代码
          console.log(6);
          resolve();
          console.log(7);
        })
        setTimeout(() => {
          console.log(8)
        }, 0)
        p.then(() => {
          console.log(9);
        })
        console.log(10);
    

    总结

    提示:这里对文章进行总结:

    以上就是今天要讲的内容,本文仅仅简单介绍了事件轮询理解和实践

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值