今天做了一道笔试题觉得很有意义分享给大家,题目如下:

setTimeout(()=>{console.log('A');
},0);
var obj={func:function () {setTimeout(function () {console.log('B')},0);return new Promise(function (resolve) {console.log('C');resolve();})}
};
obj.func().then(function () {console.log('D')
});
console.log('E');复制代码

JavaScript 都知道它是一门单线程的语言,这也就意味着 JS 无法进行多线程编程,但是 JS 当中却有着无处不在的异步概念 。要完全理解异步,就需要了解 JS 的运行核心——事件循环(event loop)。

一、什么是事件队列?

首先来看一个小小的demo

console.log('start');
setTimeout(()=>{console.log('A');
},1000);
console.log('end');
//start
//end
//A复制代码

js执行之后,程序输出 'start' 和 'end',在大约1s之后输出了 'A' 。那我们就有疑问了?为什么A不在end之前执行呢?

这是因为 setTimeout 是一个异步的函数。意思也就是说当我们设置一个延迟函数的时候,当前脚本并不会阻塞,它只是会在浏览器的事件表中进行记录,程序会继续向下执行。当延迟的时间结束之后,事件表会将回调函数添加至事件队列(task queue)中,事件队列拿到了任务过后便将任务压入执行栈(stack)当中,执行栈执行任务,输出 'A'。

事件队列是一个存储着待执行任务的队列,其中的任务严格按照时间先后顺序执行,排在队头的任务将会率先执行,而排在队尾的任务会最后执行。事件队列每次仅执行一个任务,在该任务执行完毕之后,再执行下一个任务。执行栈则是一个类似于函数调用栈的运行容器,当执行栈为空时,JS 引擎便检查事件队列,如果不为空的话,事件队列便将第一个任务压入执行栈中运行。

那么我将这个例子做一个小小的改动看一看:

console.log('start');
setTimeout(()=>{console.log('A');
},0);
console.log('end');
//start
//end
//A复制代码

可以看出,我们将settimeout第二个参数设置为0后,'A' 也总是会在 'end' 之后输出。所以究竟发生了什么?这是因为 setTimeout 的回调函数只是会被添加至事件队列,而不是立即执行。由于当前的任务没有执行结束,所以 setTimeout 任务不会执行,直到输出了 'end' 之后,当前任务执行完毕,执行栈为空,这时事件队列才会把 setTimeout 回调函数压入执行栈执行。

二、Promise的含义和基本用法?

所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

写一个小demo看一下Promise的运行机制:

let promise = new Promise(function(resolve, reject) {console.log('Promise');resolve();
});promise.then(function() {console.log('resolved.');
});console.log('Hi!');
// Promise
// Hi!
// resolved复制代码

上面代码中,Promise 新建后立即执行,所以首先输出的是Promise。然后,then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,所以resolved最后输出。

三、Macrotasks和Microtasks

Macrotasks和Microtasks 都属于上述的异步任务中的一种,他们分别有如下API:
macrotasks: setTimeout, setInterval, setImmediate, I/O, UI rendering
microtasks: process.nextTick, Promise, MutationObserver

setTimeout的macrotask, 和 Promise的microtask 有哪些不同,先来看下代码如下:

console.log(1);
setTimeout(function(){console.log(2);
}, 0);
Promise.resolve().then(function(){console.log(3);
}).then(function(){console.log(4);
});//1
//3
//4
//2复制代码

如上代码可以看到,Promise的函数代码的异步任务会优先setTimeout的延时为0的任务先执行。

原因是任务队列分为 macrotasks 和 microtasks, 而promise中的then方法的函数会被推入到microtasks队列中,而setTimeout函数会被推入到macrotasks

任务队列中,在每一次事件循环中,macrotask只会提取一个执行,而microtask一直提取,直到microsoft队列为空为止。

也就是说如果某个microtask任务被推入到执行中,那么当主线程任务执行完成后,会循环调用该队列任务中的下一个任务来执行,直到该任务队列到最后一个任务为止。

而事件循环每次只会入栈一个macrotask,主线程执行完成该任务后又会检查microtasks队列并完成里面的所有任务后再执行macrotask的任务。

四、分析本题目

setTimeout(()=>{console.log('A');
},0);
var obj={func:function () {setTimeout(function () {console.log('B')},0);return new Promise(function (resolve) {console.log('C');resolve();})}
};
obj.func().then(function () {console.log('D')
});
console.log('E');复制代码

1、首先 setTimeout A 被加入到事件队列中  ==>  此时macrotasks中有[‘A’];

2、obj.func()执行时,setTimeout B 被加入到事件队列中  ==> 此时macrotasks中有[‘A’,‘B’];

3、接着return一个Promise对象,Promise 新建后立即执行 执行console.log('C'); 控制台首次打印‘C’;

4、然后,then方法指定的回调函数,被加入到microtasks队列,将在当前脚本所有同步任务执行完才会执行。 ==> 此时microtasks中有[‘D’];

5、然后继续执行当前脚本的同步任务,故控制台第二次输出‘E’;

6、此时所有同步任务执行完毕,如上所述先检查microtasks队列完成其中所有任务,故控制台第三次输出‘D’;

7、最后再执行macrotask的任务,并且按照入队列的时间顺序,控制台第四次输出‘A’,控制台第五次输出‘B’。

五、执行js代码

分析与实际符合,NICE!

参考文章:www.cnblogs.com/tugenhua070…

还有阮老师的promise介绍:es6.ruanyifeng.com/?search=pro…

文章本人原创,转载请评论;

前端菜鸟对JavaScript的理解还有很多不足,如有错误欢迎大家指出来;

喜欢的点个赞把!

笔试题——JavaScript事件循环机制(event loop、macrotask、microtask)相关推荐

  1. 事件循环机制(event loop)

    目录 前言 一.事件循环概述 二.进程和线程 三.为什么JavaScript是单线程? 四.JavaScript如何解决单线程阻塞问题? 五.JavaScript 运行机制 六.任务队列 task q ...

  2. 带你了解事件循环机制(Event Loop)

    什么是事件循环机制? 事件循环分为两种,分别是浏览器事件循环和node.js事件循环,本文主要对浏览器事件循环进行描述. 我们都知道JavaScript是一门单线程语言,指主线程只有一个.Event ...

  3. 深入理解JavaScript的事件循环(Event Loop) 一、什么是事件循环

    深入理解JavaScript的事件循环(Event Loop) 一.什么是事件循环 JS的代码执行是基于一种事件循环的机制,之所以称作事件循环,MDN给出的解释为因为它经常被用于类似如下的方式来实现 ...

  4. js 循环 等待异步执行完再执行_JS异步执行机制——事件循环(Event Loop)

    JS异步执行机制--事件循环(Event Loop) 本文首发地址: 前端基础 | JS异步执行机制--事件循环(Event Loop)​www.brandhuang.com 先祭出一段代码,你清楚它 ...

  5. js 异步执行_JS异步执行机制——事件循环(Event Loop)

    JS异步执行机制--事件循环(Event Loop) 本文首发地址: 前端基础 | JS异步执行机制--事件循环(Event Loop)​www.brandhuang.com 先祭出一段代码,你清楚它 ...

  6. 理解浏览器和nodeJs中的事件循环(Event Loop)

    浏览器环境下 js 引擎的事件循环机制 js 引擎每次只能执行一个操作,而通常情况下操作又不止一个,因此这些操作会被依次放入一个队列中,js 引擎会按照队列中的顺序去执行操作,这个队列叫做执行栈. 当 ...

  7. JavaScript事件循环机制

    众所周知JS是一门单线程执行环境的语言,对于同步任务而言,同一时刻只能执行一个任务,后续的任务都要在当前执行的任务后面排队.这种模式在遇到一些执行时间较长的任务的时候就会出问题,会导致页面失去响应.所 ...

  8. js异步等待完成后再进行下一步操作_彻底搞懂JS事件中的循环机制 Event Loop

    我们都知道JavaScript是单线程语言,就是因为单线程的特性,就不得不提js中的同步和异步 一.同步和异步 所谓单线程,无非就是同步队列和异步队列,js代码是自上向下执行的,在主线程中立即执行的就 ...

  9. javascript事件轮询(event loop)详解

    英语原文摘自:http://blog.carbonfive.com/2013/10/27/the-javascript-event-loop-explained/ The JavaScript Eve ...

最新文章

  1. 拥抱单页网站! jQuery全屏滚动插件fullPage.js
  2. BadgeView(View上添加提醒)的应用与分析
  3. 【PC工具】更新win10关闭更新工具,接速度最快最好用的文件内容搜索工具:searchmyfiles...
  4. 创意DIY项目分享(3)
  5. C# FileSystemWatcher文件监控实例
  6. PHP开发的爱情盲盒交友系统网站源码
  7. 谈java的导入(import)
  8. 浏览器不兼容原因及解决办法
  9. 新版网易云课堂视频真实地址分析
  10. 矩形窗函数rect()和辛格函数sinc()是一组傅里叶变换对相关公式证明
  11. 手机自带浏览器的强大
  12. 生活,人艰不拆......
  13. keil 生成三角波dac0832_怎么样利用南方CASS三角网法和方格网法进行土方量计算...
  14. clappr:可扩展网页媒体播放器使用(在vue中的使用)
  15. 如何做好SQLite 使用质量检测,让事故消灭在摇篮里
  16. 关于移动端适配的几个方法,快速构建响应式网站必备知识
  17. 树莓linux pi密码,树莓派 忘记密码的解决方法
  18. 中山纪念中学2015年暑假集训总结
  19. P4408 [NOI2003] 逃学的小孩(树的直径)
  20. XDOJ 分配宝藏 C

热门文章

  1. c++ 获取64位进程模块地址_针对银行木马BokBot核心模块的深入分析
  2. 字段定义_两大高招逐浪CMS中定义省地市县三级字段显示方式
  3. python困难_Python开发总感觉困难重重,可能是你没用上这些开发工具
  4. javascript中变量
  5. C++11实战——多线程的日志类
  6. 解决Android中No resource found that matches android:TextAppearance.Material.Widget.Button.Inverse问题...
  7. 数据库中存储日期的字段类型究竟应该用varchar还是datetime ?
  8. 【html 及 HTML5所有标签汇总】★★★
  9. Hibernate级联操作 注解
  10. Timer TimeTask Handler