JavaScript是单线程,也就是说JS的堆栈中只允许有一类任务在执行,不可以同时执行多类任务。在读js文件时,所有的同步任务是一条task,当然了,每一条task都是一个队列,按顺序执行。而如果在中途遇到了setTimeout这种异步任务,就会将它挂起,放到任务队列中去执行,等执行完毕后,如果有callback,就把callback推入到Tasks中去,注意,是把异步任务的完成时的callback推进去,等待执行,而microtasks什么时候执行呢?只要JS stack栈清了,它就执行,它和异步任务不一样的是,它不会新开一个任务队列,就是新开一个task。常见的microtask有promise事件,MutationObserver对象。

这里我补充一下MutationObserver对象:就是监控某个范围内的DOM树如果发生变化时就会触发一些相应的事件,这个是DOM4里面定义的,用来替换DOM3里的Mutation事件,兼容性移动端没什么问题,安卓4.4,ios 6/7,但是IE就比较惨了,只能11以上。

常见的用法如下:

// Firefox和Chrome早期版本中带有前缀
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver// 选择目标节点
var target = document.querySelector('#some-id');// 创建观察者对象
var observer = new MutationObserver(function(mutations) {mutations.forEach(function(mutation) {console.log(mutation.type);});
});// 配置观察选项:
var config = { attributes: true, childList: true, characterData: true }// 传入目标节点和观察选项
observer.observe(target, config);// 随后,你还可以停止观察
observer.disconnect();

这里其实只要关注几点:

第一是构造函数的第一个参数是一个函数,就是回调,但是这个回调要注意的是,它不是立即回调,而是等所有的变化都结束的时候才会统一回调,这里比较坑的就是这个,所谓的所有变化都结束实际上就是指一个task里面,

第二是如果前一个mutation还没有触发回调,那么后续的mutation也不会触发。

回到上面。

所谓的event loop之所以说是一个循环,是因为每次JavaScript执行完Tasks队列中的一个task之后,它都会回过头去看一下是否有待执行的task了,如果有那就继续执行,这是一个不断循环重复的过程,因为你同步任务task执行完成后,挂起的异步任务可能还在任务队列里执行,暂时还没有callback,而异步任务执行完成后,callback开一个新的task,然后进入到Tasks队列中等候,而JS却不知道你已经在那儿排队了,所以没办法,只能通过不断循环的方式来确保每一个待执行的task都能被及时执行。它的逻辑大概如下,具体的下篇再讲:

while (queue.waitForMessage()) {queue.processNextMessage();
}

这里只是做一个类比。

其实通过上面这个机制,我们可以看出一些问题:

比如:

1,setTimeout一定准时吗,不一定,如果它前面的task执行时间超过了它设置的时间,那它必须得等那个task执行完成之后才能执行,第二个参数的时间值并不代表该时间以后执行setTimeout callback,而是该时间以后将callback这个新的task推入到Tasks里面去等待执行。所以不要写什么setTimeout 0这种以为能立即执行的了,而且W3C规范,setTimeout最小值只能为4。

2,如果我有一个异步队列callback特别长,要执行好久好久,而此时我又触发了一个新的事件,那怎么办?没办法啊,只能等它这个callback执行完才能执行啊,就比如你是一个click事件,你触发了click对应的function,将这个新的task推入到Tasks中去,而此时并不会立即执行,因为前面还有没执行完的任务,所以会造成点击没效果,因为它还在等待前一个异步callback执行完。所以不要以为异步任务是异步的,就可以随意写一堆逻辑,如果太复杂了,也会造成用户操作没反应这种问题,虽然比较少。

3,关于click事件,这里单独说一下,看一个例子:

<div class='outer'><div class='inner'></div>
</div>

// Let's get hold of those elements
var outer = document.querySelector('.outer');
var inner = document.querySelector('.inner');// Let's listen for attribute changes on the
// outer element
new MutationObserver(function() {console.log('mutate');
}).observe(outer, {attributes: true
});// Here's a click listener…
function onClick() {console.log('click');setTimeout(function() {console.log('timeout');}, 0);Promise.resolve().then(function() {console.log('promise');});outer.setAttribute('data-random', Math.random());
}// …which we'll attach to both elements
inner.addEventListener('click', onClick);
outer.addEventListener('click', onClick);

如果此时我点击里面那个小块儿inner,控制台会打印什么?

click
promise
mutate
click
promise
mutate
timeout
timeout

这里为什么会触发两次这个函数,很简单,因为冒泡,但是我们能看出一个问题,promise和mutate是微任务,只有JS主线程栈空了它们才会执行,说明每一次冒泡执行完后都会空一下,但是这里要注意的是,虽然主线程空了,但是不代表这里的click事件因为冒泡而被分成了两个task,实际上还是一个task(叫Dispatch click),而timeout虽然很快,但还是排在了两次冒泡的Dispatch click后面,因为前面是一个task,它是新开的一个task,必须等前面一个task执行完毕。那这里也能看出,微任务不一定是在当前task结束的时候才会执行,这里来看个图

这里可以看出,所有的微任务都在Microtasks这个队列中等待执行,只要JS stack一清空,它就立即执行,而是否一定是某个task执行完成呢,不一定,只要空了,它就执行。上面那个click冒泡就是很好的证明。但是可以确定的是,微任务一定是在一次事件循环event loop的结尾处执行。

下面来看一个坑爹的,还是上面那些代码,如果不是人为主动点击触发,而是改用js主动触发事件,就比如inner.click(),会是什么结果呢?来看:

click
click
promise
mutate
promise
timeout
timeout

意不意外?惊不惊喜?timeout就不解释了,同上,但是为什么click是连续执行了,为什么mutate只执行了一次,而且还是夹在了promise中间,针对这几个点,解释一下:

1,为什么click连续执行两次:先来看为什么上面那个不是连续执行,原因很简单,因为js栈空了,所以先执行了微任务,那这次为什么没有先执行微任务,那就是说明JS stack没有空嘛,这里作者给出的解释是,click()会导致事件同步分派,所以调用的脚本.click()仍然处于回调之间的堆栈中。上述规则确保微任务不会中断正在执行的JavaScript。这意味着我们不会在两者之间处理Microtasks队列,而是在它们之后。。。。总之就是如果是js调用的函数,JS堆栈不会空。

2,为什么mutate只执行了一次:当第一次执行到mutate的时候,它被直接插进了Microtasks队列中等待执行,而刚刚说到了MutationObserver这个对象的实例有个特点,当前一个挂起的这个对象还没解决的时候,后续的是不会处理的,所以只有一次,因此只有一个mutate被夹到了两个promise之间。

以上就是整个Tasks,Microtasks,任务队列等等专业知识的解释。

关于event loop的详解,请看后续。

end

转载于:https://www.cnblogs.com/yanchenyu/p/8398777.html

JavaScript:再谈Tasks和Microtasks相关推荐

  1. 我看朴灵评注阮一峰的《JavaScript 运行机制详解:再谈Event Loop》

    阮一峰和朴灵对我来说都是大牛,他们俩的书我都买过,阮老师的译作<软件随想录>和朴灵的<深入浅出node.js>.这个事情已经过了4个月了,所以我拿来讲应该也没啥问题. 这件事情 ...

  2. 从Promise来看JavaScript中的Event Loop、Tasks和Microtasks

    原文 github.com/creeperyang- 主题 Promise 看到过下面这样一道题: (function test() {setTimeout(function() {console.l ...

  3. Tasks(任务), microtasks(微任务), queues(队列) and schedules(回调队列)

    原文:What is the Execution Context & Stack in JavaScript? git地址:Tasks(任务), microtasks(微任务), queues ...

  4. 再谈JSON -json定义及数据类型

    再谈json 近期在项目中使用到了highcharts ,highstock做了一些统计分析.使用jQuery ajax那就不得不使用json, 可是在使用过程中也出现了非常多的疑惑,比方说,什么情况 ...

  5. 再谈HTTP2性能提升之背后原理—HTTP2历史解剖

    即使千辛万苦,还是把网站升级到http2了,遇坑如<phpcms v9站http升级到https加http2遇到到坑>. 因为理论相比于 HTTP 1.x ,在同时兼容 HTTP/1.1 ...

  6. 再谈编程范式-程序语言背后的思想

    link link 编程范式 托马斯.库尔提出"科学的革命"的范式论后,Robert Floyd在1979年图灵奖的颁奖演说中使用了编程范式一词.编程范式一般包括三个方面,以OOP ...

  7. 马云再谈对钱没有兴趣;比尔·盖茨:微软原本可以击败 Android!TypeScript 3.7 发布 | 极客头条...

    作者 | 唐小引 出品 | CSDN(ID:CSDNnews) 快来收听极客头条音频版吧,智能播报由标贝科技提供技术支持. 「极客头条」-- 技术人员的新闻圈! CSDN 的读者朋友们早上好哇,「极客 ...

  8. 再谈js对象数据结构底层实现原理-object array map set

    2019独角兽企业重金招聘Python工程师标准>>> 如果有java基础的同学,可以回顾下<再谈Java数据结构-分析底层实现与应用注意事项>:java把内存分两种:一 ...

  9. 再谈编程范式—程序语言背后的思想

    编程范式 托马斯.库尔提出"科学的革命"的范式论后,Robert Floyd在1979年图灵奖的颁奖演说中使用了编程范式一词.编程范式一般包括三个方面,以OOP为例: 1,学科的逻 ...

最新文章

  1. 配置Haproxy增加日志记录功能
  2. C++特化的应用——类型萃取
  3. vue实现表格组件,带分页
  4. jfinal出现sql injection violation, multi-statement not allow : select count(*) select a.name,
  5. 复位 stm32_分析一个关于STM32 芯片异常复位的经典案例!
  6. HCIBench_2.3.1部署_VSAN_测试工具
  7. 数学建模-7.多元线性回归分析
  8. 在Mac OS X Lion上安装Ruby On Rails
  9. 5分钟学会双拼 双拼输入法 最简单的双拼入门教学 图文教程
  10. python股票量化交易从入门到实践df_python股票量化交易从入门到实践df_Python股票量化交易从入门到实践/金融科技系列......
  11. 这一代绝非低智商——读《低智商社会@大前研一》
  12. linux脚本while死循环,shell编程之while死循环
  13. oracle数据库教程ppt,Oracle 数据库入门教程 PPT
  14. 什么是Linux内存管理中的RSS和VSZ
  15. HTML px em pt长度单位(像素 相对长度 点)
  16. 一片文章带你了解汇编语言
  17. 2022年淘宝天猫双十一预售红包优惠券满减活动什么时候开始天猫淘宝双11预售定金可以退款吗?
  18. 不用充电的监控摄像头有哪几种供电方式
  19. 1000瓶水和1瓶毒水还有10只老鼠
  20. ECDSA (Elliptic Curve Digital Signature Algorithm)

热门文章

  1. 深圳大学梁臻博士提出EEGFuseNet高维脑电图混合无监督深度特征表征与融合模型及其在情绪识别中的应用...
  2. Python-EEG工具库MNE中文教程(9)-参考电极应用
  3. [UE4]C++实现动态加载的问题
  4. AutoX江苏超级工厂首曝光!机械臂随处可见,车辆出厂就能完全无人驾驶
  5. 王咏刚分享DeeCamp三年成功经验:学生超自主,导师很顶尖,批量培养AI人才不是梦...
  6. Ruiy自我识人做事领悟录ing
  7. Windbg 基础命令 《第一篇》
  8. 听君今一言,似读半月书
  9. 苏宁、国美-OPM战略
  10. Winform中DataGridView绑定IList数据源后的排序