博客
关于我
JavaScript之运行机制,以及Event-Loop的讲解
阅读量:194 次
发布时间:2019-02-28

本文共 2564 字,大约阅读时间需要 8 分钟。

这章节采用先题目后原理的说法为大家讲解。

一、引入题目

我们先看一道题目吧!

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

你们觉得上面打印的顺序是什么呢?如果你不了解JS运行机制,那么你就犯错吧。其实上面打印的是1 3 2,是不是很奇怪呢?这是为什么呢?相信小伙伴现在已经出现了1000多个疑问,不着急,听我慢慢分解吧。

二、单线程是什么?为什么JS要用单线程?

单线程大白话就是你同一个时间你就只能做一件事情。JavaScript的单线程,与它的用途有关。JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。就比如说,你更新的时候同时点击删除,不可能吧。单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着

三、理解任务队列(消息队列)

JavaScript语言的设计者意识到这个问题,将所有任务分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。下面看两个例子,这样子比较直观。

下面这个代码打印的是什么:

console.log(1)while(true){}console.log(2)

答案:1。 因为while是同步任务

再来看看下面打印的是什么:

console.log(1)setTimeout(()=>{  console.log(2)})while(true){}

答案:1,按照单线程原理先执行同步任务,再执行异步任务,while为同步任务,setTimeout为异步任务。

四、理解Event Loop

首先我们还是看一个例子:

for (var i = 0; i < 5;i++){  setTimeout(()=>{    console.log(i)  }, 0)}

你们觉得会打印什么? 答案是5个5。

这里要说的是异步任务的放入时间和执行时间(也就是异步执行的运行机制)如下:①②为备注。

  1. 所有同步任务都在主线程上执行,形成一个执行栈
  2. 当执行到setTimeout异步任务时候,不先放入任务队列中,而是timer模块先拿走,
  3. 等setTimeout时间①到了再放入“任务队列”中,
  4. 等到运行栈里面的同步任务全部运行完毕后再到“任务队列”中读取异步任务②,这个时候setTimeout里面的函数体就会变成同步任务。
  5. (2,3,4)的反复循环称为Event Loop。只要主线程空了,就会去读取"任务队列",这就是JavaScript的运行机制。

①【当第二个参数缺省时为时间,默认为 0;当指定的值小于 4 毫秒,则增加到 4ms(4ms 是 HTML5 标准指定的,对于 2010 年及之前的浏览器则是 10ms);也就是说至少需要4毫秒,该setTimeout()拿到任务队列中。】

②【一旦"执行栈"中的所有同步任务执行完毕(即for循环结束,此时i已经为5),系统就会读取已经存放"任务队列"的setTimeout()(有五个),于是答案是输出5个5】

像下面的东西都会放在异步队列中。 

  • setTimeout和setlnterval
  • DOM事件(就是我们平常点击按钮的时候会卡顿,就是这种情况)
  • ES6中的Promise
  • Ajax异步请求

 五、Event loop的执行顺序

  • 首先 JavaScript 引擎会执行一个宏任务,注意这个宏任务一般是指主干代码本身,也就是目前的同步代码
  • 执行过程中如果遇到微任务,就把它添加到微任务任务队列中
  • 宏任务执行完成后,立即执行当前微任务队列中的微任务,直到微任务队列被清空
  • 微任务执行完成后,开始执行下一个宏任务
  • 如此循环往复,直到宏任务和微任务被清空

宏任务包括 script 同步标签, setTimeout ,setInterval ,setImmediate ,I/O ,UI rendering

微任务包括 process.nextTick ,promise ,MutationObserver,其中 process.nextTick 为 Node 独有。

综合例子:会打印出什么?

setTimeout(()=>console.log('a'))    Promise.resolve().then(()=>console.log('b')).then(      ()=>Promise.resolve('c').then(        (data) => {          setTimeout(()=>console.log('d'))          console.log('e')          return data        }      )    ).then((data)=>{      console.log(data)    })    new Promise((resolve)=>{      console.log('f')      resolve('g')    }).then((res)=>{      console.log(res)      }    );    console.log('h');

答案:

f, h, b, g, e, c, a, d

解析:b和g是属于同一层的then,所以,这两个会被依次输出的。

题目2:

console.log(1);let a = setTimeout(() => {console.log(2)}, 0);console.log(3);Promise.resolve(4).then(b => {console.log(b);clearTimeout(a);});console.log(5);

答案:1 3 5 4

转载地址:http://wgni.baihongyu.com/

你可能感兴趣的文章
navicat创建连接 2002-can‘t connect to server on localhost(10061)且mysql服务已启动问题
查看>>
Navicat可视化界面导入SQL文件生成数据库表
查看>>
Navicat向sqlserver中插入数据时提示:当 IDENTITY_INSERT 设置为 OFF 时,不能向表中的标识列插入显式值
查看>>
Navicat因导入的sql文件中时间数据类型有参数而报错的原因(例:datetime(3))
查看>>
Navicat如何连接MySQL
查看>>
navicat导入.sql文件出错2006- MySQLserver has gone away
查看>>
Navicat导入海量Excel数据到数据库(简易介绍)
查看>>
Navicat工具Oracle数据库复制 or 备用、恢复功能(评论都在谈论需要教)
查看>>
Navicat工具中建立数据库索引
查看>>
navicat工具查看MySQL数据库_表占用容量_占用空间是多少MB---Linux工作笔记048
查看>>
navicat怎么导出和导入数据表
查看>>
Navicat怎样同步两个数据库中的表
查看>>
Navicat怎样筛选数据
查看>>
Navicat报错connection is being used
查看>>
Navicat报错:1045-Access denied for user root@localhost(using passwordYES)
查看>>
Navicat控制mysql用户权限
查看>>
navicat操作mysql中某一张表后, 读表时一直显示正在载入,卡死不动,无法操作
查看>>
Navicat连接mysql 2003 - Can't connect to MySQL server on ' '(10038)
查看>>
Navicat连接mysql数据库中出现的所有问题解决方案(全)
查看>>
Navicat连接Oracle出现Oracle library is not loaded的解决方法
查看>>