最近做的项目中,涉及到了JavaScript中Promise的用法,于是做了一点测试,发现没有想象中的那么简单,水很深,所以找来N先生(我的Mentor),想得到专业的指导。N先生也不尽知,但N先生查源码能力了不起,一小时之内解决了问题,还给我了一篇英文参考文献,拜读后,觉得有必要写一篇随笔,记录所得。

一. 问题

如果输入以下代码,会得到什么样的输出结果?如果连N先生这样在这个行业内浸染了12年的人都无法在最初给出正确的话,想必很多人还是很难回答正确的。不过在看下面的内容前,不妨猜测一下:D

 console.log('script start');

 setTimeout(function() {

   console.log('setTimeout');

 }, 0);

 Promise.resolve().then(function() {

   console.log('promise1');

 }).then(function() {

   console.log('promise2');

 });

 console.log('script end');

正确的顺序为:

script start

script end

promise1

promise2

setTimeout

当然由于不同浏览器支持的问题,在某些特定版本的浏览器下,setTimeout会在promise1,pormise2的前面,比如Microsoft Edge, Firefox 40, iOS Safari 和 desktop Safari 8.0.8。

这看起来像是资源竞争(race condition)的问题,但实际上并不是。

二. 为什么会是以上的顺序

要理解为什么是以上的顺序发生,我们需要掌握事件队列(event loop)是如何处理JavaScript Tasks 和 microtasks的。如果你是第一次碰到这个问题,那么,请深吸一口气,接着往下看:D

每个工作线程(Web worker)都有自己的事件队列,他们各自相互不影响,每个线程的事件队列,都会对进队的tasks任务进行处理。如果有很多的task在一个工作线程的队列里等待处理,那么,需要由浏览器来决定执行的先后顺序。

这时,浏览器就可以给那些性能敏感(performance sensitive)的task优先权,比如对用户输入的反应。

Tasks会被排定好,于是browser可以按顺序将它带给JavaScript/DOM。在task和task的中间,浏览器或许会渲染更新。比如鼠标点击的回调函数就可以排定一个task,再比如,setTimeOut。

setTimeOut会在给定的时间之后,对回调函数排定一个Task,script end是第一个task,而setTimeout是独立的另一个task,这也就是为什么setTimeout会在scrript end之后输出。

Mircrotasks经常会被排定在当前执行脚本结束之后立即执行,这样的task比如想把事情做成异步,但又不想建立一个全新的task。Microtasks queue会在当前没有JavaScript事件正在执行,并且已经到了每个task结束的时候处理。任何新入队的microtask在正在执行microtask queue时,也会被马上处理。在以上的例子中,promise的回调函数就是microtask。

一旦一个promise解决了,它会将自己的回调函数加入microtask queue。这保证了即使promise及时解决了,它的回调函数还是异步执行的。这就是为什么promise1,promise2为什么在script end后面执行。Microtask总是在下一个task开始前发生。

三. 为什么有些浏览器会有不同的表现呢

其实是因为“将promise作为task还是microtask引起的”。针对这个已经有了很多的讨论,但舆论基本是同意将promise作为microtask的。理由我就不列了,相信大家也能自己琢磨的到:D

Cheers,

You have a nice day!

Lei

JavaScript tasks, microtasks, queues and schedules的更多相关文章

  1. [Javascript] Use requestIdleCallback to schedule JavaScript tasks at an optimal time

    JavaScript is single-threaded, which can present some problems when creating an interactive user exp ...

  2. 深入理解 JavaScript 事件循环(二)— task and microtask

    引言 microtask 这一名词是 JS 中比较新的概念,几乎所有人都是在学习 ES6 的 Promise 时才接触这一新概念,我也不例外.当我刚开始学习 Promise 的时候,对其中回调函数的执 ...

  3. javascript引擎执行的过程的理解--执行阶段

    一.概述 同步更新sau交流学习社区(nodeJSBlog):javascript引擎执行的过程的理解--执行阶段 js引擎执行过程主要分为三个阶段,分别是语法分析,预编译和执行阶段,上篇文章我们介绍 ...

  4. 深入理解JavaScript事件循环机制

    前言 众所周知,JavaScript 是一门单线程语言,虽然在 html5 中提出了 Web-Worker ,但这并未改变 JavaScript 是单线程这一核心.可看HTML规范中的这段话: To ...

  5. 每个JavaScript工程师都应懂的33个概念

    摘要: 基础很重要啊! 原文:33 concepts every JavaScript developer should know 译文:每个 JavaScript 工程师都应懂的33个概念 作者:s ...

  6. [译] 深入理解 JavaScript 事件循环(二)— task and microtask

    引言 microtask 这一名词是 JS 中比较新的概念,几乎所有人都是在学习 ES6 的 Promise 时才接触这一新概念,我也不例外.当我刚开始学习 Promise 的时候,对其中回调函数的执 ...

  7. 每个 JavaScript 工程师都应懂的33个概念

    简介 这个项目是为了帮助开发者掌握 JavaScript 概念而创立的.它不是必备,但在未来学习(JavaScript)中,可以作为一篇指南. 本篇文章是参照 @leonardomso 创立,英文版项 ...

  8. 从event loop规范探究javaScript异步及浏览器更新渲染时机

    异步的思考 event loops隐藏得比较深,很多人对它很陌生.但提起异步,相信每个人都知道.异步背后的“靠山”就是event loops.这里的异步准确的说应该叫浏览器的event loops或者 ...

  9. 总结:JavaScript异步、事件循环与消息队列、微任务与宏任务

    本人正在努力学习前端,内容仅供参考.由于各种原因(不喜欢博客园的UI),大家可以移步我的github阅读体验更佳:传送门,喜欢就点个star咯,或者我的博客:https://blog.tangzhen ...

随机推荐

  1. 百度地图 判断marker是否在多边形内

    昨天画了圆形,判marker是否存在圆形内.今天来画多边形,判断marker在多边形内. 需要引入一个js      <script type="text/javascript&quo ...

  2. Evolutionary Computing: 2. Genetic Algorithm(1)

    本篇博文讲述基因算法(Genetic Algorithm),基因算法是最著名的进化算法. 内容依然来自博主的听课记录和教授的PPT. Outline 简单基因算法 个体表达 变异 重组 选择重组还是变 ...

  3. python的类和对象——进阶篇

    写在前面的话 终于,又到了周五.当小伙伴们都不再加班欢欢喜喜过周末的时候,我刚刚写完这一周的游戏作业,从面对晚归的紧皱眉头到现在的从容淡定,好像只有那么几周的时间.突然发现:改变——原来这么简单.很多 ...

  4. POSIX信号

    POSIX 表示可移植操作系统接口(Portable Operating System Interface ,缩写为 POSIX ),POSIX标准定义了操作系统应该为应用程序提供的接口标准,是IEE ...

  5. JS的学习体会与分享

    最近在学习js这门计算机网络的新知识.相比与之前所学习的html和css这两门编程语言,js 还是有很多的不同之处,它相比于前两门知识还是有一定的难度和逻辑性的.今天跟大家分享一篇文章,希望能从中获得 ...

  6. loading

    <!doctype html> <html> <head> <title>实惠福利</title> <meta charset=&qu ...

  7. 电脑自动访问g.ceipmsn.com

    电脑老师每隔1分3秒自动访问 g.ceipmsn.com这个域名 经过排查发现是TeamViewer8在作怪,好像是一直在激活引起的 关闭TeamViewer程序就好了.

  8. HTTP 错误 500.23 - Internal Server Error 解决方法

    分析原因:在安装完成后IIS已经支持ASP和ASP.NET 2.0,需要注意的是.NET站点的应用程序池应选用Classic .NET AppPool,而不能用默认的DefaultAppPool,否则 ...

  9. EventSource

    <!doctype html> <html> <head> <meta charset="UTF-8"> <title> ...

  10. bzoj 1005 1211 prufer序列总结

    两道题目大意都是根据每个点的度数来构建一棵无根树来确定有多少种构建方法 这里构建无根树要用到的是prufer序列的知识 先很无耻地抄袭了一段百度百科中的prufer序列的知识: 将树转化成Prufer ...