事件循环机制详解

事件循环机制

  1. 单线程与多线程

    • 单线程
    • 多线程
  2. 任务队列与事件循环机制

单线程与多线程

线程分为单线程和多线程

  1. 单线程

    1. 只能同时进行一个任务,后面的任务想要进行就必需等前面的任务完成了

    2. 可以保证代码执行顺序,但是容易导致阻塞

  2. 多线程

    1. 同时可以执行多个任务,谁也不挡谁

    2. 可以解决阻塞问题,但是会改变代码执行顺序。改变顺序后可能让程序变得稍微难理解了一些

js是单线程

解决单线程和多线程的问题

同步模式:代码按顺序执行,前面没有执行完后后面的代码不会执行
异步模式:代码可以同时执行,前面没执行完的代码不会影响后面的代码

在js中有哪些是异步模式?

1、回调函数
2、事件
3、定时器
4、AJAX
5、发布/订阅(设计模式)
6、Promise(ES6)
7、Generator函数(ES6)
8、async函数(ES7)

同步模式的代码如下,只有当2000000个span创建完后才会输出 ’ 终于走到我这里了’

 for(var i = 0;i<2000000;i++){
        var span = document.createElement('span');
        span.innerHTML = i;
        document.body.appendChild(span);
    }
console.log('终于走到我这里了');

异步模式的代码如下

  setTimeout(function(){
     console.log('老沙最慢')
   },100);

   setTimeout(function(){
       console.log('八戒其次')
   },0);

   console.log('猴哥最快')

代码会依次输出 —猴哥最快、八戒其次、老沙最慢

任务队列与事件循环机制

js引擎给我们提供了任务队列
任务队列的读取方式:先进先出,后进后出

任务队列(消息队列)分类:

  • macrotask(宏任务队列)
    1、DOM操作
    2、用户交互(事件)
    3、网络任务(ajax)
    4、history traversal任务(h5当中的历史操作)
    5、定时器
  • microtask(微任务队列)
    1、Promise.then
    2、process.nextTick(nodejs中的一个异步操作)
    3、MutationObserver(H5里面增加的,用来监听DOM节点变化的)

如下代码运行结果是猴哥最快、八戒其次、老沙最慢

setTimeout(function () {
  console.log('老沙最慢'); 
}, 100);

setTimeout(function () {
    console.log('八戒第二'); 
}, 0);

console.log('猴哥最快'); 

代码运行过程详解如下
1、代码走到script标签时,会创建一个全局环境globalEC,然后将globalEC放入上下文执行栈(call stack)中,
2、接着走到第一个setTimeout的时候,因为执行时间为100ms,所以,先不放入宏任务队列,
3、接着走到第二个setTimeout的时候,执行时间为0ms,放入宏任务队列,100ms时间到了之后,才放入第一个setTimeout里面的函数
4、走到console.log(‘猴哥最快’)的时候,因为不是异步,所以放入globalEC里面,然后执行console里面的,执行完后就被删除
5、stack为空的时候,接下来就找微任务队列,如果微任务没有任务,就找宏任务队列

在这里插入图片描述

再来一个,下面的代码有宏任务也有微任务,
浏览器依次输出 ‘全局的’ 、 ‘Promise的’ 、 ‘then的’ 、 ‘定时器的’,
Promise.then才会被放入微任务队列中,看下图

 console.log('全局的');		
  setTimeout(function(){
  	console.log('定时器的');	
  },0);
  new Promise(function(resolve){
      console.log('Promise的');
      resolve();	
  }).then(function(){
  	  console.log('then的');	
  }); 

在这里插入图片描述
再来看另一个代码

	<input type="text">
	<button>点击</button>
	<input type="text">
 var inputs = document.querySelectorAll('input');
 var btn = document.querySelector('button');

  btn.onclick = function () {
      //inputs[0].value+=' button';
      setTimeout(function () {
          inputs[0].value += ' button'; // body button
      }, 0);
  };

  document.body.onclick = function () {
      inputs[0].value += ' body';
  };

在js代码中第四行被注释的代码,在不用setTimeout的时候,点击button的时候会输出‘button body’,这里存在着事件冒泡。但是加上了setTimeout的时候,会输出‘body button’,这也和事件循环机制有关

那么下面这段代码为啥要加setTimeout呢,并且时间还是0,加了有啥意义呢?

inputs[1].onkeydown = function () {
    var This = this;
    setTimeout(function () {
        console.log(This.value);
    }, 0);
};

你们可以自己试一下,不加setTimeout的时候,在输入框中输入数据的时候看控制台打印的什么,在输入框为空的时候,我们输入一个a,控制台上会打印一个空白,在输入一个s的时候,打印a。这是为什么呢?

 因为事件发生的非常快,浏览器接受的内容没有事件快,浏览器需要渲染,但是事件不需要渲染,所以取不到内容
这时候我们可以通过定时器解决这个问题----由于setTimeout里面的console现在是一个异步,
所以会把console内容放入宏任务队列--console其实是在第二次event loop发生的时候才执行,因为第二次的时候浏览器已经渲染完了

下面总结一下

  事件循环	Event Loop 			
 	 1、它是HTML规范中定义的或者是DOM中定义的,并不是ECMAScript定义的
			1、https://www.w3.org/TR/html5/webappapis.html#event-loops
			2、https://html.spec.whatwg.org/#event-loops 		 
   	2、它包括两种,一种在浏览器上下文里,一种在web workers里(HTML5)
   	3、它是一种分配模式,把任务队列里的任务分配给全局执行上下文进行执行 			
   	4、执行顺序
			1、先把微任务队列里的任务拿出来交给全局执行上下文执行
			2、再把宏任务队列里的任务拿出来交给全局执行上下文执行
			3、不断的循环 			

注意:
1、任务队列(异步代码)里的代码最终还是要放到全局执行上下文(同步的代码)里去执行
2、当全局执行上下文里的代码都执行完了才去执行任务队列里的代码
3、宏任务与微任务的区别是:宏任务会再下一次的Event Loop里执行,微任务会在本次Event Loop里执行

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值