JavaScript运行机制-浏览器的Event loop

JS中所谓的任务包括执行JS代码、对用户的输入(如:鼠标点击、键盘输入)作出反应、处理异步的网络请求等等,按照执行方式可以分为分为同步任务和异步任务,现在我们清楚的知道浏览器的一个渲染进程只分配给js一个主线程(JS引擎线程),用来执行代码,这意味着所有的任务最终都要进入主线程通过调用栈来执行。

JS同步任务

同步任务指的是在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。

JS异步任务

异步任指的是不会进入主线程,而是被放到任务队列 (task queue)中排队等待执行,当主线程的任务全部执行完,主线程会从"任务队列"中读取事件执行代码,这个过程是循环不断的,所以整个的这种运行机制又称为事件循环(Event Loop)。

Event Loop是一种执行的规则,打个比方,假设JS主线程是一排道的马路,现在有ABCDE五辆车顺序通行,但现在B车要1分钟后出发,C车一旦雨停就出发,D车等人送来驾照出发,这样会导致E车在一定时间内只能等待,效率很低,为了提高效率,可以让A车和E车进入车道正常排序行驶,B、C、D先去一边,一旦能走了,车道上又没有车,就可以上道出发。
与之类似,假设我们代码中有ABCDE五个任务排队等待执行,当任务进入执行栈,主线程会判断是同步还是异步任务,如果是同步任务,则立即执行,执行完毕出栈;

如果是异步任务,则将其回调函数挂起,即:进入Event Table并注册回调函数,将任务交给异步模块(如:浏览器内核的 DOM Binding 模块、 timer 模块、network 模块)进行处理,当异步有了结果,Event Table将注册的函数移入Event Queue中。

JS主线程执行栈为空的时候,会去Event Queue读取对应的函数,进入主线程执行,不断的重复这个过程,就是事件循环Event Loop。

在这里插入图片描述

  function taskA(){
    console.log('A出发')
  }
  function taskB(){
    setTimeout(function () {
      console.log('时间到了,B出发');
    },60000);
  };
  function taskC(){
    var btn=document.getElementById("btn");
    btn.addEventListener("click", function () {
      console.log('雨停了,C出发');
    });
  };
  function taskD(){
    var xhr = new XMLHttpRequest();  //创建一个XHR对象
    xhr.open("GET","https://www.fastmock.site/mock/d6b39fde63cbe98a4f2fb92ff5b25a6d/api/A");
    xhr.onload = function() {
      if(xhr.status=='200'){
        console.log('驾照送来了,D出发');
      }
    };
    xhr.send(null);
  };
  function taskE(){
    console.log('E出发')
  };
  taskA();
  taskB();
  taskC();
  taskD();
  taskE();

JS运行机制

Event Loop是Javascript的执行机制,也是js实现异步的一种方法,具体说:
JS的执行时单线程的,首先判断JS是同步还是异步,同步就进入主线程,异步的进入Event Table并注册函数,由浏览器其他模块处理,JS引擎继续执行代码,当异步有了结果,Event Table会将这个函数移入Event Queue。
主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行,循环不断的重复这个过程。

宏任务和微任务

任务分为同步任务和异步任务,异步任务又分为宏任务和微任务,浏览器中他们的执行顺序是同步->微任务->宏任务。

宏任务(macro-task)

由宿主环境(这里指浏览器)提供的叫宏任务,如上边提到的setTimeout、网络请求Ajax、用户I\O(输入输出)。
包含:script(整体代码), setTimeout, setInterval, I/O, UI rendering;

微任务(micro-task ):

由ECMAScript提供的叫微任务,如ES6提供的Promise 。
包含: Promise, Object.observe, MutationObserver

  • Promise对象用来传递异步操作的消息,为异步编程提供了一种解决方案。请参考:ES6-Promise对象获取异步操作的消息
    下面的例子中会打印出1-2-5-4-3,:
    1、JS首先执行同步任务,打印出1,打印出2(这条语句是同步任务),然后将setTimeout的回调加入宏队列,继续执行,resolve方法被调用,Promise状态变为resolved,再将then里的回调加入微队列,继续执行打印出5。
    2、接着执行微任务,因为Promise的异步已经完成,所以执行微任务,打印4
    3、执行宏任务,打印3

      console.log('1');
      new Promise(function(resolve) {
        console.log('2');
        setTimeout(function(){
          console.log('3');
        })
        resolve();
      }).then(function() {
        console.log('4');
      })
      console.log('5');
    
  • Object.observe API可以被称为一种“可以对任何对象的属性值修改进行监视的事件处理函数”。

  • MutationObserver 是现代浏览器提供的用来检测 DOM 变化的网页接口。

宏任务和微任务的执行顺序

宏任务需要多次事件循环才能执行完,微任务是一次性执行完。
对应宏任务和微任务,有两种任务队列,宏观任务队列(macrotask queue)和是微观任务队列(microtask queue)。JS解析器先执行主线程同步任务,执行栈为空后一次性执行微任务,再去执行宏任务队列的第一个任务,执行完毕后从微队列中取出位于队首的回调任务,放入调用栈中执行,执行完后microtask queue长度减1,继续取出位于队首的任务,放入调用栈Stack中执行,以此类推,直到把microtask queue中的所有任务都执行完毕。
注意:如果在执行microtask的过程中,又产生了microtask,那么会加入到队列的末尾,也会在这个周期被调用执行。

参考:https://www.jianshu.com/p/4516ad4b3048
参考:http://www.ruanyifeng.com/blog/2014/10/event-loop.html
参考:https://segmentfault.com/q/1010000008960948/a-1020000008962511

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值