最近做的项目中,涉及到了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. css+div打造三角形(箭头)

    在很多网站都见过这样的箭头,之前我一直以为是图片,直到今天才知道原来可以用css做.开始看代码没太看懂,后来自己试了几遍才恍然大悟.贴出来分享下.(大神请直接忽略) 先看代码: HTML部分就是一个单 ...

  2. [分享] 晒一晒我的Windows7_SP1封装母盘(多图,附部分工具),老鸟飘过~

    [分享] 晒一晒我的Windows7_SP1封装母盘(多图,附部分工具),老鸟飘过~ 大宝贝1 发表于 2012-8-9 18:01:57 https://www.itsk.com/thread-20 ...

  3. jq知识总结

    jQuery   jQuery基本选择器: id选择器     $("#div1") class选择器   $(".div1") 元素选择器   $(" ...

  4. CentOS 下 rpm包与 yum 安装与卸载

    rpm包的安装:      1.安装一个包 # rpm -ivh 2.升级一个包 # rpm -Uvh 3.移走一个包 # rpm -e 4.安装参数 --force 即使覆盖属于其它包的文件也强迫安 ...

  5. iOS开发UI篇—Kvc简单介绍

    ios开发UI篇—Kvc简单介绍 一.KVC简单介绍 KVC key valued coding 键值编码 KVC通过键值间接编码 补充: 与KVC相对的时KVO,即key valued observ ...

  6. Citrix 虚拟化笔记

    利用win7 x64/Vmware workstation 10练习Citrix虚拟化 [安装Xenserver 6.2] 1)硬盘空间不足:要求最小8G 2)不支持硬件虚拟化:找到建立的XENSER ...

  7. pycharm 使用小结

    1.pycharm 自动换行,显示行号,缩进向导 在代码右侧右键 2.自动注释/取消注释 ctrl + /

  8. 实现手机扫描二维码页面登录,类似web微信-第一篇,业务分析

    转自:http://www.cnblogs.com/fengyun99/p/3541249.html 关于XMPP组件的文章,先休息两天,好歹已经完整的写了一份. 这两天,先实现一套关于web微信扫描 ...

  9. AngularJs的UI组件ui-Bootstrap分享(四)——Datepicker Popup

    Datepicker Popup是用来选择日期的控件,一般和文本框一起使用,功能和Jquery的插件My97DatePicker一样.在Datepicker Popup内部使用了ui-bootstra ...

  10. 2、android Service 详细用法

    定义一个服务 在项目中定义一个服务,新建一个ServiceTest项目,然后在这个项目中新增一个名为MyService的类,并让它继承自Service,完成后的代码如下所示: ? 1 2 3 4 5 ...