Event loops秒懂

简介

JS是一种单线程脚本语言,为什么要设计成单线程?

举例说明,假设JS是多线程脚本语言,A线程修改了DOM,B线程删除了DOM,一旦B线程先执行完,DOM被删除了,A线程就会报错,为了避免类似这种问题,JS被设计为单线程

单线程的问题是一次只能做一件事,要做第二件事,必须等第一件事先做完。假如有个需求是每5分钟更新一次数据,用setInterval去计时,那么这个页面JS永远无法做其他事了,线程一直被setInterval占用着。为了让JS可以同时执行多个任务,引入了Event loops(事件循环)机制

Event loops分为2种队列,task队列、microtask队列,业界一般把tasks队列称为宏任务,microtask翻译过来叫微任务。

task队列和microtask队列执行顺序是怎样的?

代码刚开始执行时,整体代码就是一个task,立即执行这个task,在执行过程中

  • 遇到setTimeout、setInterval、I/O、setImmediate(Nodejs环境)就往task队列里push
  • 遇到Promise.then/catch/finally、MutationObserver、process.nextTick(Nodejs环境)就往microtask队列里push

每执行完一个task,就会查看microtask队列里有没有待执行的任务,如果有,则按先进先出的原则依次执行其中的任务,执行完了再回到task队列,取下一个task执行;如果没有,就直接执行下一个task,以此类推,这就是Event loops

案例

按照以上规则,思考以下代码输出顺序

// 先自己思考一下输出顺序
console.log('script start'); setTimeout(function () {
console.log('timeout');
}, 0); Promise.resolve().then(function () {
console.log('promise');
}).then(function () {
console.log('then');
}); console.log('script end');

分析:

  1. 整体代码做为第一个task,从上到下开始执行
  2. 输出script start
  3. 遇到setTimeout(),push到task队列,等待执行
  4. 遇到Promise第一个then(),push到microtask队列,等待执行
  5. 输出script end
  6. 第一个task执行完成,查看microtask队列,有任务,开始执行
  7. 输出promise,遇到第二个then(),push到microtask队列
  8. 输出刚刚push的then
  9. microtask队列执行完成,取下一个task执行
  10. 输出timeout

输出顺序为:script start -> script end -> promise -> then -> timeout

升级,return Promise

将上面例子的Promise升级了一下,假设Promise.then内部又有Promise,怎么分析?

Promise.resolve().then(function () {
console.log('promise');
return new Promise((resolve, reject) => {
console.log('inner promise');
resolve();
}).then(() => {
console.log('inner then1');
}).then(() => {
console.log('inner then2');
})
}).then(function () {
console.log('then');
});

分析:

  1. 整体代码为第一个task,从上到下开始执行
  2. 遇到第一个then,push到microtask队列
  3. 第一个task执行完成,查看microtask队列,有任务,开始执行
  4. 输出 promise
  5. 进入内部new Promise,输出 inner promise
  6. 遇到内部new Promise第一个then,push到microtask队列
  7. 输出刚刚push的 inner then1
  8. 遇到内部new Promise第二个then,push到microtask队列
  9. 输出刚刚push的 inner then2
  10. 内部new Promise执行完,外部promise第一个then拿到返回值,继续往下,遇到它的第二个then,push到microtask队列
  11. 输出刚刚push的 then

输出顺序为:promise -> inner promise -> inner then1 -> inner then2 -> then

注意:then链式调用时,如果前面的then方法return了一个新Promise对象,后面的then会等待这个新Promise对象状态发生变化后,才会执行,换句话说,两个then的执行由异步变成同步了,如果把return去掉呢?

变化,无return Promise

Promise.resolve().then(function () {
console.log('promise');
new Promise((resolve, reject) => {
console.log('inner promise');
resolve();
}).then(() => {
console.log('inner then1');
}).then(() => {
console.log('inner then2');
})
}).then(function () {
console.log('then');
});

分析:

  1. 整体代码为第一个task,从上到下开始执行
  2. 遇到第一个then,push到microtask队列
  3. 第一个task执行完成,查看microtask队列,有任务,开始执行
  4. 输出 promise
  5. 进入内部new Promise,输出 inner promise
  6. 遇到内部new Promise第一个then,push到microtask队列,前6步跟上面一样
  7. 此时,外部Promise对象的第一个then里的同步代码已经执行完了,接着执行它的第二个then,push到microtask队列
  8. 继续执行microtask,输出 inner then1
  9. 到了内部new Promise的第二个then,push到microtask队列
  10. 继续执行microtask,输出 then
  11. 最后输出 inner then2

输出顺序为:promise -> inner promise -> inner then1 -> then -> inner then2

总结:return去掉之后,前面的then执行完同步代码就会跳到下一个then

思考

最后思考一个问题,下面这个错误能被catch捕获到吗?为什么?

new Promise(function (resolve, reject) {
setTimeout(function () {
throw new Error('test')
}, 0)
resolve('ok');
}).catch(err => {
console.error(err);
});

熟悉了Event loops,回答这个问题就很容易:不能捕获到。因为错误在setTimeout内部抛出,setTimeout和.catch并不在同一个task执行,抛出错误的时候,catch已经执行完了。

觉得不错,点个star吧Github

Event loops秒懂的更多相关文章

  1. 协程系列之Event Loops

    Event Loops 事件循环 事件是由程序的一部分在特定条件下发出的消息,循环是在某种条件下完成并执行某个程序直到它完成的构造,因此,事件循环是一个循环,它允许用户订阅事件传输并注册处理程序/回调 ...

  2. queueMicrotask & EventLoop & macrotask & microtask

    queueMicrotask https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/queueMicro ...

  3. [转]How WebKit’s Event Model Works

    原文:https://homes.cs.washington.edu/~burg/projects/timelapse/articles/webkit-event-implementation/ Fi ...

  4. event & signals & threads

    The Event Systemhttp://doc.qt.io/qt-4.8/eventsandfilters.html Each thread can have its own event loo ...

  5. nodejs(五)同步异步--BLOCKING THE EVENT LOOP

    1.BLOCKING THE EVENT LOOP Node and JavaScript runtimes in general are single-threaded event loops. O ...

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

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

  7. 浏览器组成、线程及event loop

    浏览器组成 User interface: a. Every part of the browser display, except the window. b. The address bar, b ...

  8. 事件轮询 event loop

    Understanding the node.js event loop The first basic thesis of node.js is that I/O is expensive: So ...

  9. How an Event Enters a Cocoa Application

    How an Event Enters a Cocoa Application An event is a low-level record of a user action that is usua ...

随机推荐

  1. python3下scrapy爬虫(第三卷:初步抓取网页内容之抓取网页里的指定数据)

    上一卷中我们抓取了网页的所有内容,现在我们抓取下网页的图片名称以及连接 现在我再新建个爬虫文件,名称设置为crawler2 做爬虫的朋友应该知道,网页里的数据都是用文本或者块级标签包裹着的,scrap ...

  2. algorithm-question

    主键都相同,选择排序和插入排序谁快 选择排序:比较N*(N-1)/2,交换0:插入排序:比较N-1,交换0:插入排序更 大专栏  algorithm-question快 逆序数组,插入排序与选择排序 ...

  3. jenkins使用(4)-发邮件

    邮箱配置 邮件触发器:达到条件就会发邮件 对单独的服务设置邮件 系统设置如下 回到单个任务的设置 配置中的主题设置如下: 回到单个任务的设置 配置中如下: 选择工作空间中的一个文件 发送多个附件: 邮 ...

  4. python的列表list和集合set操作

    以下是一些python的list和set的基本操作 1.list的一些操作 list = [1, 2, 3] list.append(5) print(list) list.extend([7, 8] ...

  5. <JZOJ4726>种花

    挺有意思的贪心 神奇的贪心 #include<cstdio> #include<iostream> #include<cstring> #include<al ...

  6. 吴裕雄--天生自然python学习笔记:beautifulsoup库的使用

    Beautiful Soup 库简介 Beautiful Soup提供一些简单的.python式的函数用来处理导航.搜索.修改分析树等功能.它是一个工具箱,通过解析文档为用户提供需要抓取的数据,因为简 ...

  7. [转]<版本一>写代码的小女孩

    天冷极了,下着雪,又快黑了.这是NOIP的前夜.在这又冷又黑的晚上,一个衣衫破烂的小女孩在机房敲着代码.她从班里逃出来的时候还拿着一本算导,但是有什么用呢?那是一本很破旧的书——那么大,一向是她妈妈垫 ...

  8. 从又一家外卖被Uber收购,看美团打车未来

    别以为Uber在中国失利,连优步中国都被滴滴收购了,就认为Uber已经不行了.其实从全球范围内来看,Uber还是相当强势的--创始人的那些破事儿不算在内.此外,更重要的是Uber已经不单单是在打车业务 ...

  9. hive、Hbase、mysql的区别

    1.Hive和HBase的区别 1)hive是sql语言,通过数据库的方式来操作hdfs文件系统,为了简化编程,底层计算方式为mapreduce. 2)hive是面向行存储的数据库. 3)Hive本身 ...

  10. 攻防世界Mobile6 app1 XCTF详解

    XCTF_app1 先安装看看 点击芝麻开门之后会弹出“年轻人不要耍小聪明噢” 这大概就能看懂是点击之后进行判断,那就直接去看JEB,看看判断条件是什么 V1是输入的字符串,V2获取包信息(百度的), ...