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

栏目: JavaScript · 发布时间: 6年前

内容简介:今天做了一道笔试题觉得很有意义分享给大家,题目如下:JavaScript 都知道它是一门单线程的语言,这也就意味着 JS 无法进行多线程编程,但是 JS 当中却有着无处不在的首先来看一个小小的demo

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

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代码

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

分析与实际符合,NICE!

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

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

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

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

喜欢的点个赞把!


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

App研发录:架构设计、Crash分析和竞品技术分析

App研发录:架构设计、Crash分析和竞品技术分析

包建强 / 机械工业出版社 / 2015-10-21 / CNY 59.00

本书是作者多年App开发的经验总结,从App架构的角度,重点总结了Android应用开发中常见的实用技巧和疑难问题解决方法,为打造高质量App提供有价值的实践指导,迅速提升应用开发能力和解决疑难问题的能力。本书涉及的问题有:Android基础建设、网络底层框架设计、缓存、网络流量优化、制定编程规范、模块化拆分、Crash异常的捕获与分析、持续集成、代码混淆、App竞品技术分析、项目管理和团队建设等......一起来看看 《App研发录:架构设计、Crash分析和竞品技术分析》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具