前言

相信对于刚学习JavaScript的新手来说,去理解JS中的事件循环原理以及异步执行过程比较困难,但是这是JS必须要会的基础知识,逃避不能解决问题,笔者曾经也被这个知识点困扰过,现根据以往的经验编写此文章,旨在帮助大家彻底搞懂它们以及自我巩固,话不多说,进入正题。

注意:本篇文章主要是基于浏览器环境,Node环境没有研究过暂不讨论

引言

我们先来小试牛刀,看看下面这段代码是怎么执行的,例1:

  setTimeout(() => {
    console.log('time')
  });
  new Promise((resolve, reject) => {
    console.log('p1');
    resolve();
  }).then(() => {
    console.log('res')
  });
  console.log(1);
 // 输出: p1 1 res time

怎么样?你想的输出结果和实际的输出结果是一样的吗?如果是一样的说明你对事件循环有一定的了解,但是你真的已经清楚的知道了事件循环的原理吗?让我们继续往下看。

为什么会有事件循环?

JS是单线程的

众所周知:JavaScript 是一门单线程语言,也就是说,同一个时间只能做一件事。这是因为 Javascript 这门脚 本语言诞生的使命所致——JavaScript 是为处理页面中用户的交互,以及操作 DOM 而诞生的。比如我们对 某个 DOM 元素进行添加和删除操作,不能同时进行。 应该先进行添加,之后再删除。

单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。这样所导致的问题是: 如果 JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉

为了解决这个问题,利用多核 CPU 的计算能力,HTML5 提出 Web Worker 标准,允许JavaScript 脚本创建多个线程,但是子线程完全受主线程控制。于是,JS 中出现了同步任务和异步任务。

同步任务和异步任务 

  • 同步任务:

同步任务都在主线程上执行,形成一个执行栈。在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;

  • 异步任务:

不进入主线程、而进入”任务队列”的任务,当主线程中的任务运行完了,才会从”任务队列”取出异步任务放入主线程执行。JS 的异步是通过回调函数实现的。异步任务相关回调函数添加到任务队列中(任务队列也称为消息队列)

注意:异步任务执行机制在这里描述的比较笼统,主要方便大家理解,具体细节在后面的“宏任务与微任务”中会详细介绍

JS的事件循环就是基于同步任务与异步任务来展开的,让我们继续往下看:

JS事件循环

事件循环是JavaScript实现异步的一种方法,也是JavaScript的执行机制

如图:

当一个脚本第一次执行的时候,js引擎会解析这段代码,并将其中的同步代码按照执行顺序加入执行栈中,然后从头开始执行。

当遇到异步任务时不会一直等待事件的返回结果,而是将事件挂起(即交给其他线程处理,上图是指Web Worker),继续执行执行栈中的其他任务。

当异步事件返回结果时,js将异步事件callback函数放入队列中,被放入队列中的异步事件不会立即回调,等到当前执行栈中的任务都执行完成,处于闲置状态的主线程按照队列顺序将处于首位事件的callback函数放入执行栈中,执行该函数的同步代码,如果遇到了异步事件,同样也会将其回调函数放入事件队列中…

如此反复,就形成了一个循环,这也是被称为“事件循环(EventLoop)”的原因。

js事件循环的基本原理已经描述清楚,但是异步任务之间也有所不同:

任务队列实际上分为两个:宏任务队列和微任务队列。上图只表示了一个是为了便于大家理解事件循环,下面就是事件循环更细节的东西了

宏任务与微任务

上面讲到,js在执行异步任务时,回调函数会被放在js的任务队列中,实际上,回调函数的类别不同,执行的优先级也不同。

不同的优先级被分为两类,一类是宏任务(Micro task),一类是微任务(Macro task)。

回调函数是微任务时,会被放在微任务队列,回调函数是宏任务时,会被放在宏任务队列。

微任务的优先级高于宏任务,当主线程的任务执行完成时,会首先去执行微任务队列中首位的回调函数,当微任务队列中为空时,才回去执行宏任务队列中的回调函数。

常见的宏任务有哪些?

  • 包括整体代码 script
  • setTimeout()
  • setInterval()
  • setImmediate()(Node独有)
  • I/O
  • UI 交互事件(浏览器独有)
  • requestAnimationFrame() (浏览器独有)

常见的微任务有哪些?

  • Promise.then(); Promise.cath()
  • async/await
  • process.nextTick() (Node独有)
  • MutationObserver() (H5新增,监听DOM树变化)
  • Object.observe() (异步监视对象修改,已废弃)

注意:new Promise()属于同步任务,但是Promise.then(); Promise.cath()属于异步任务的微任务

执行过程总结(重点)

现在我们对事件循环有了深入了解了,但是它们的执行过程还不是很清晰,我们再把执行过程弄清楚了以后就能游刃有余了。

同步任务 —> 微任务 —> 宏任务...

  • 先执行所有同步任务,碰到异步任务放到任务队列中
  • 同步任务执行完毕,开始执行当前所有的异步任务
  • 先执行任务队列里面所有的微任务,如果执行过程中又产生了微任务也会在本次执行过程中执行(即在下一个宏任务执行之前执行,可以看看案例1)
  • 然后执行一个宏任务(从宏任务队列头部pop出一个宏任务进执行栈,该任务中的具体代码也如步骤1执行)
  • 然后再执行所有的微任务(此时的微任务一般为步骤4中产生出的微任务)
  • 再执行一个宏任务,再执行所有的微任务·······依次类推到执行结束。

3-6的这个循环称为事件循环Event Loop

案例挑战

学会了吗?让我们来做几个案例巩固一下吧

案例1:

const promise = new Promise((resolve, reject) => {
    resolve("10")
  }).then(res => {
    console.log("res1:", res)    //res1: hahaha
    return 9
  }).then(res => {
    console.log("res2:", res)    //res2: 9
    return 8
  }).then(res => {
    console.log("res3:", res)    //res3: 8
    let promise2=new Promise((resolve,reject)=>{
        resolve("p2")
      }).then(res=>{
        console.log(res)
        setTimeout(function(){
          console.log("setTimeout2")
        },0)
      })
  })
  console.log('aaa')
  setTimeout(function(){
    console.log("setTimeout1")
  },0)
  const promise1 = new Promise((resolve, reject) => {
    console.log("p1")
    resolve(989)
}).then(res => {
    console.log(res)
    return 990
}).then(res=>{
  console.log(res)
  return 991
}).then(res=>{
  console.log(res)
  return 0
})
/*输出结果:
aaa
p1
res1: 10
989
res2: 9
990
res3: 8
991
p2
setTimeout1
setTimeout2
*/

案例2:

console.log('1');
// 定义注解 setTimeout_1 用于下文使用方便
setTimeout(function() {
    console.log('2');
    process.nextTick(function() {
        console.log('3');
    })
    new Promise(function(resolve) {
        console.log('4');
        resolve();
    }).then(function() {
        console.log('5')
    })
})
process.nextTick(function() {
    console.log('6');
})
new Promise(function(resolve) {
    console.log('7');
    resolve();
}).then(function() {
    console.log('8')
})
// setTimeout_2
setTimeout(function() {
    console.log('9');
    process.nextTick(function() {
        console.log('10');
    })
    new Promise(function(resolve) {
        console.log('11');
        resolve();
    }).then(function() {
        console.log('12')
    })
})
// 输出结果:  1 7 6 8 2 4 3 5 9 11 10 12

案例3:

console.log('1');    
setTimeout(function() {
    console.log('2');
    process.nextTick(function() {
        console.log('3');
    })
    new Promise(function(resolve) {
        console.log('4');
        resolve();
    }).then(function() {
        console.log('5')
    })
})
process.nextTick(function() {
    console.log('6');
})
new Promise(function(resolve) {
    console.log('7');
    resolve();
}).then(function() {
    console.log('8')
})
setTimeout(function() {
    console.log('9');
    process.nextTick(function() {
        console.log('10');
    })
    new Promise(function(resolve) {
        console.log('11');
        resolve();
    }).then(function() {
        console.log('12')
    })
})
// 输出结果:1 7 6 8 2 4 3 5 9 11 10 12

以上就是JavaScript事件循环剖析宏任务与微任务的详细内容,更多关于JavaScript 事件循环宏任务微任务的资料请关注Devmax其它相关文章!

JavaScript事件循环剖析宏任务与微任务的更多相关文章

  1. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  2. HTML5数字输入仅接受整数的实现代码

    这篇文章主要介绍了HTML5数字输入仅接受整数的实现代码,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  3. amaze ui 的使用详细教程

    这篇文章主要介绍了amaze ui 的使用详细教程,本文通过多种方法给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  4. html5简介_动力节点Java学院整理

    这篇文章主要介绍了html5简介,用于指定构建网页的元素,这些元素中的大多数都用于描述网页内容,有兴趣的可以了解一下

  5. ios 8 Homescreen webapp,关闭和打开iPad停止javascript

    我有一个适用于iPad的全屏HTML5网络应用程序,并且刚刚安装了IOS8来试用它,它一切正常,直到你关闭并重新启动iPad.一旦web应用程序重新启动javascript就会停止并加载新页面不会重新启动它.在iPad上的Safari中打开同一页面时,关闭和打开iPad会继续按预期工作.其他人注意到了这个或想出了一个解决方案吗?解决方法这似乎是我在iOS8.1.1更新中解决的.

  6. iOS 6 javascript与object.defineProperty的间歇性问题

    当访问使用较新的Object.defineProperty语法定义属性的对象的属性时,有没有其他人注意到新iOS6javascript引擎中的间歇性错误/问题?https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperty我正在看到javascript失败的情况,说

  7. ios – 如何使用JSExport导出内部类的方法

    解决方法似乎没有办法将内部类函数导出到javascript.我将内部类移出并创建了独立的类,它起作用了.

  8. 静音iOS推送通知与React Native应用程序在后台

    我有一个ReactNative应用程序,我试图获得一个发送到JavaScript处理程序的静默iOS推送通知.我看到的行为是AppDelegate中的didReceiveRemoteNotification函数被调用,但是我的JavaScript中的处理程序不会被调用,除非应用程序在前台,或者最近才被关闭.我很困惑的事情显然是应用程序正在被唤醒,并且它的didReceiveRemoteNotifi

  9. ios – 内存泄漏与UIWebView和Javascript

    清楚地包含一个Javascript文件到我的HTML是使UIWebView泄漏内存.当我重复使用相同的UIWebView对象时,或者每当我有内容实例化一个新的漏洞时,会出现泄漏的事实,导致我认为必须有一些JavaScript文件被loadHTMLString处理,导致泄漏.有人知道如何解决这个问题吗?

  10. iOS应用程序的UI自动化测试如何与乐器和Javascript

    从WWDC2010视频会议中了解iOS应用程序的自动化UI测试,但没有实践.从代码项目project,我们可以有一个例子.这个问题在这里听到有涉及这个的人.任何限制?解决方法我建议从AlexWollmer开始使用thisblogpost.他创建了一个非常有用的JavaScript库:tuneup_jswithtest()函数,它允许测试分离和有用的帮助者以及为自动化仪器编写测试的断言.

随机推荐

  1. js中‘!.’是什么意思

  2. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  4. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  6. JavaScript面向对象编程入门教程

    这篇文章主要介绍了JavaScript面向对象编程的相关概念,例如类、对象、属性、方法等面向对象的术语,并以实例讲解各种术语的使用,非常好的一篇面向对象入门教程,其它语言也可以参考哦

  7. jQuery中的通配符选择器使用总结

    通配符在控制input标签时相当好用,这里简单进行了jQuery中的通配符选择器使用总结,需要的朋友可以参考下

  8. javascript 动态调整图片尺寸实现代码

    在自己的网站上更新文章时一个比较常见的问题是:文章插图太宽,使整个网页都变形了。如果对每个插图都先进行缩放再插入的话,太麻烦了。

  9. jquery ajaxfileupload异步上传插件

    这篇文章主要为大家详细介绍了jquery ajaxfileupload异步上传插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. React学习之受控组件与数据共享实例分析

    这篇文章主要介绍了React学习之受控组件与数据共享,结合实例形式分析了React受控组件与组件间数据共享相关原理与使用技巧,需要的朋友可以参考下

返回
顶部