一、JS单线程、异步、同步概念

  从上一篇说明vue nextTick的文章中,多次出现“事件循环”这个名词,简单说明了事件循环的步骤,以便理解nextTick的运行时机,这篇文章将更为详细的分析下事件循环。在此之前需要了解JS单线程,及由此产生的同步执行环境和异步执行环境。
  众所周知,JS是单线程(如果一个线程删DOM,一个线程增DOM,浏览器傻逼了~所以只能单着了),虽然有webworker酱紫的多线程出现,但也是在主线程的控制下。webworker仅仅能进行计算任务,不能操作DOM,所以本质上还是单线程。
  单线程即任务是串行的,后一个任务需要等待前一个任务的执行,这就可能出现长时间的等待。但由于类似ajax网络请求、setTimeout时间延迟、DOM事件的用户交互等,这些任务并不消耗 CPU,是一种空等,资源浪费,因此出现了异步。通过将任务交给相应的异步模块去处理,主线程的效率大大提升,可以并行的去处理其他的操作。当异步处理完成,主线程空闲时,主线程读取相应的callback,进行后续的操作,最大程度的利用CPU。此时出现了同步执行和异步执行的概念,同步执行是主线程按照顺序,串行执行任务;异步执行就是cpu跳过等待,先处理后续的任务(CPU与网络模块、timer等并行进行任务)。由此产生了任务队列与事件循环,来协调主线程与异步模块之间的工作。
 

二、事件循环机制

                                  事件循环示例图
 
  如上图为事件循环示例图(或JS运行机制图),流程如下:
    step1:主线程读取JS代码,此时为同步环境,形成相应的堆和执行栈;
    step2:  主线程遇到异步任务,指给对应的异步进程进行处理(WEB API);
    step3:  异步进程处理完毕(Ajax返回、DOM事件处罚、Timer到等),将相应的异步任务推入任务队列;
    step4: 主线程执行完毕,查询任务队列,如果存在任务,则取出一个任务推入主线程处理(先进先出);
    step5: 重复执行step2、3、4;称为事件循环。
  执行的大意:
    同步环境执行(step1) -> 事件循环1(step4) -> 事件循环2(step4的重复)…
  其中的异步进程有:
    a、类似onclick等,由浏览器内核的DOM binding模块处理,事件触发时,回调函数添加到任务队列中;
    b、setTimeout等,由浏览器内核的Timer模块处理,时间到达时,回调函数添加到任务队列中;
    c、Ajax,由浏览器内核的Network模块处理,网络请求返回后,添加到任务队列中。
 

三、任务队列

  如上示意图,任务队列存在多个,同一任务队列内,按队列顺序被主线程取走;不同任务队列之间,存在着优先级,优先级高的优先获取(如用户I/O);
  3.1、任务队列的类型
    任务队列存在两种类型,一种为microtask queue,另一种为macrotask queue。
    图中所列出的任务队列均为macrotask queue,而ES6 的 promise[ECMAScript标准]产生的任务队列为microtask queue。
            
  3.2、两者的区别
    microtask queue:唯一,整个事件循环当中,仅存在一个;执行为同步,同一个事件循环中的microtask会按队列顺序,串行执行完毕;
    macrotask queue:不唯一,存在一定的优先级(用户I/O部分优先级更高);异步执行,同一事件循环中,只执行一个。
 
  3.3、更完整的事件循环流程    
    将microtask加入到JS运行机制流程中,则:
      step1、2、3同上,
      step4:主线程查询任务队列,执行microtask queue,将其按序执行,全部执行完毕;
      step5:主线程查询任务队列,执行macrotask queue,取队首任务执行,执行完毕;
      step6:重复step4、step5。
    microtask queue中的所有callback处在同一个事件循环中,而macrotask queue中的callback有自己的事件循环。
    简而言之:同步环境执行 -> 事件循环1(microtask queue的All)-> 事件循环2(macrotask queue中的一个) -> 事件循环1(microtask queue的All)-> 事件循环2(macrotask queue中的一个)...
    利用microtask queue可以形成一个同步执行的环境,但如果Microtask queue太长,将导致Macrotask任务长时间执行不了,最终导致用户I/O无响应等,所以使用需慎重。
 

四、示例、验证  

            console.log('1, time = ' + new Date().toString())
setTimeout(macroCallback, 0);
new Promise(function(resolve, reject) {
console.log('2, time = ' + new Date().toString())
resolve();
console.log('3, time = ' + new Date().toString())
}).then(microCallback); function macroCallback() {
console.log('4, time = ' + new Date().toString())
} function microCallback() {
console.log('5, time = ' + new Date().toString())
}

  结合第二节与第三节的分析,此处的执行流程应为:

    同步环境:1 -> 2 -> 3

    事件循环1(microCallback):5

    事件循环2(macroCallback):4

  运行结果如下:

    

  运行结果与预期一致,验证了在不同类型的任务队列中,microtask queue中的callball将优先执行。

    总结:由此我们了解事件循环的机制,同时了解了任务队列、JS主线程、异步操作之间的相互协作;同时认识了两种任务队列:macrotask queue、microtask queue,它们由不同的标准制定,microtask queue对应ECMAScript的promise属性(ES6)和 DOM3的MutationObserver,文中说明了两者在事件循环中的运行情况及区别;在今后的异步操作中,通过灵活运用不同的任务队列,提升用户交互性能,给出更加的响应和视觉体验;同时,通过JS的事件循环机制,可以更清楚JS代码的执行流,从而更好的控制代码,更有效、更好的为业务服务。 

JS 事件循环机制 - 任务队列、web API、JS主线程的相互协同的更多相关文章

  1. js事件循环机制辨析

     对于新接触js语言的人来说,最令人困惑的大概就是事件循环机制了.最开始这也困惑了我好久,花了我几个月时间通过书本,打代码,查阅资料不停地渐进地理解他.接下来我想要和大家分享一下,虽然可能有些许错误的 ...

  2. js 事件循环机制 EventLoop

    js 的非阻塞I/O  就是由事件循环机制实现的 众所周知  js是单线程的 也就是上一个任务完成后才能开始新的任务 那js碰到ajxa和定时器.promise这些异步任务怎么办那?这时候就出现了事件 ...

  3. Node.js 事件循环机制

    Node.js 采用事件驱动和异步 I/O 的方式,实现了一个单线程.高并发的 JavaScript 运行时环境,而单线程就意味着同一时间只能做一件事,那么 Node.js 如何通过单线程来实现高并发 ...

  4. js事件循环机制 (Event Loop)

    一.JavaScript是单线程单并发语言 什么是单线程 主程序只有一个线程,即同一时间片断内其只能执行单个任务. 为什么选择单线程? JavaScript的主要用途是与用户互动,以及操作DOM.这决 ...

  5. js事件循环机制(Event Loop)

    javascript从诞生之日起就是一门  单线程的  非阻塞的  脚本语言,单线程意味着,javascript代码在执行的任何时候,都只有一个主线程来处理所有的任务,非阻塞靠的就是 event lo ...

  6. JS - 事件循环和任务队列

    栈.堆和队列 栈:函数调用形成栈 堆:对象的内容分配在堆中 队列:一个 JavaScript 运行时包含了一个待处理的消息队列.在事件循环期间依次处理队列中的消息 事件循环 之所以称之为事件循环,是因 ...

  7. js事件循环机制

    本文参考链接:https://www.jianshu.com/p/cf47bc0bf2ab 一.先搞懂两个东西:堆和栈 栈由操作系统自动分配释放,用于存放函数的参数值.局部变量等一些基本的数据类型,其 ...

  8. JS:事件循环机制、调用栈以及任务队列

    点击查看原文 写在前面 js里的事件循环机制十分有趣.从很多面试题也可以看出来,考察简单的setTimeout也就是考察这个机制的. 在之前,我只是简单地认为由于函数执行很快,setTimeout执行 ...

  9. js的事件循环机制和任务队列

    上篇讲异步的时候,提到了同步队列和异步队列的说法,其实只是一种形象的称呼,分别代表主线程中的任务和任务队列中的任务,那么此篇我们就来详细探讨这两者. 一.来张图感受一下 如果看完觉得一脸懵逼,请继续往 ...

随机推荐

  1. js事件的绑定与移除

    事件的绑定分为3类: <div id='clickEvent'>点击事件</div> 在DOM元素中直接绑定 <div onclick="alert('4567 ...

  2. Maven 项目 启动时 解决3 字节的 UTF-8 序列的字节 3 无效

    "org.activiti.bpmn.exceptions.XMLException: 3 字节的 UTF-8 序列的字节 3 无效." Maven 项目启动时,由于读XML配置文 ...

  3. Android Studio将引用第三方jar包的library打包成jar包

    在该module的build.gradle中添加 task makeJar(type: Jar) { archiveName 'mysdk.jar' from('build/intermediates ...

  4. kubernetes1.7.6 ha高可用部署

    写在前面:  1. 该文章部署方式为二进制部署. 2. 版本信息 k8s 1.7.6,etcd 3.2.9 3. 高可用部分 etcd做高可用集群.kube-apiserver 为无状态服务使用hap ...

  5. 学习python一个月盘点

    1,看了30多个视频,是一些基本操作的.粗略的了解了语法,函数,循环,正则表达,将视频内容全部敲入文本: 2,看了100页的书,习题不太会,进展很慢.第五章10以后的题都没做: 3,看C++的书,有点 ...

  6. angular-指令

    ng-app 作用域 ng-init 声明 module 模块 ng-model 双向绑定 ng-bind 绑定 angular是一个MVC框架:即 M------------------module ...

  7. maven项目(多模块)

    在eclipse下构建maven项目,该项目由多个子模块组成. 1.创建一个父项目 NEW -->project-->maven-->maven Project,点击下一步,进入ne ...

  8. Oracle 树操作、递归查询(select…start with…connect by…prior)

    一.Oracle中start with…connect by prior子句用法 connect by 是结构化查询中用到的,其基本语法是:select … from tablename start ...

  9. 【备忘】ASP.NET MVC 5 升级到 ASP.NET Core MVC 的部分变化

    正在将一个 .net 4.5 的项目(MVC 5)升级到 .net core 2.1,中间遇到了许多的修改,记在下面,帮大家少走弯路. System.Drawing 下面很多类已经不存在(如Bitma ...

  10. LeetCode题解33.Search in Rotated Sorted Array

    33. Search in Rotated Sorted Array Suppose an array sorted in ascending order is rotated at some piv ...