javascript是单线程执行的程序,也就是它只有一条主线,所有的程序都是逐行“排队”执行,在这种情况下可能存在一些问题,比如说setTimeout、ajax等待执行的时间较长,就会阻塞后续代码的执行,使得整个程序执行的耗时非常久,那么为了应对这样一个问题,javascript代码在执行的时候,是有几个“通道”的。
 
首先是调用栈,执行耗时较短的操作,耗时较长的操作先放置到任务队列中,任务队列又分为宏任务(macro-task)和微任务(micro-task),微任务中队列中放置的是 promise.then、aysnc、await 这样操作,宏任务队列中放置的是 setTimeout、ajax、onClick事件,等调用栈的任务执行完成再轮询微任务队列,微任务队列中任务执行完成之后再执行宏任务。
 
这里提到了栈和队列,简单说一下这两种数据结构,栈是一种后进先出的结构,只能从尾部进入,从尾部删除,拿生活中的场景来打比方,就好像自助餐的餐盘,最先放的盘子在最底下,最后放的盘子在最上面,需要把最上面的盘子一个个拿走,才能拿到最下面的盘子。
而队列,是一种先进先出的结构,从尾部进入,从头部删除,就像我们去排队买东西,先去的同学可以先买到。
 
 
再回到事件循环机制(event loop),不阻塞主进程的程序放入调用栈中,压入栈底,执行完了就会弹出,如果是函数,那么执行完函数里所有的内容才会弹出,而阻塞主进程的程序放入任务队列中,他们需要“排队”依次执行。
 
首先来个简单的例子,判断以下程序的执行顺序
new Promise(resolve => {
console.log('promise');
resolve(5);
}).then(value=>{
console.log('then回调', value)
}) function func1() {
console.log('func1');
} setTimeout(() => {
console.log('setTimeout');
}); func1();

创建一个promise的实例就是开启了一个异步任务,传入的回调函数,也叫做excutor 函数,会立刻执行,所以输入promise,使用resolve返回一个成功的执行结果,then函数里的执行会推入到微任务队列中等待调用栈执行完成才依次执行。

向下执行发现定义了一个func1的函数,函数此时没有被调用,则不会推入调用栈中执行。程序继续往下,发现调用setTimeout函数,将打印的setTimeout推入宏任务队列,再往下执行调用函数func1,将func1推入调用栈中,执行func1函数,此时输出fun1。
 
调用栈里所有的内容都执行完成,开始轮询微任务队列,输入then回调5,最后执行宏任务队列,输入setTimeout
 
 
再来看一个复杂的例子
setTimeout(function () {
console.log("set1");
new Promise(function (resolve) {
resolve();
}).then(function () {
new Promise(function (resolve) {
resolve();
}).then(function () {
console.log("then4");
});
console.log("then2");
});
}); new Promise(function (resolve) {
console.log("pr1");
resolve();
}).then(function () {
console.log("then1");
}); setTimeout(function () {
console.log("set2");
}); console.log(2); queueMicrotask(() => {
console.log("queueMicrotask1")
}); new Promise(function (resolve) {
resolve();
}).then(function () {
console.log("then3");
});

setTimeout执行的回调函数("set1")直接被放置到宏任务队列中等待,Promise的excutor函数立刻执行,首先输入 pr1,Promise.then函数("then1")放入微任务队列中等待,下面的setTimeout执行的回调函数("set2")也被放置到宏任务队列中,排在("set1")后面,接下来调用栈中输出2,queueMicrotask表示开启一个微任务,与Promise.then函数效果一致,("queueMicrotask1")放入微任务队列中,再往下执行,new Promise的excutor函数立刻执行,then函数("then3")放到微任务队列中等待,此时调用栈出已依次输入pr1,2。

创建一个promise的实例就是开启了一个异步任务,传入的回调函数,也叫做excutor 函数,会立刻执行,所以输入promise,使用resolve返回一个成功的执行结果,then函数里的执行会推入到微任务队列中等待调用栈执行完成才依次执行。
 
向下执行发现定义了一个func1的函数,函数此时没有被调用,则不会推入调用栈中执行。程序继续往下,发现调用setTimeout函数,将打印的setTimeout推入宏任务队列,再往下执行调用函数func1,将func1推入调用栈中,执行func1函数,此时输出fun1。
 
调用栈里所有的内容都执行完成,开始轮询微任务队列,输入then回调5,最后执行宏任务队列,输入setTimeout
 
所以最后的输出结果为:
pr1
2
then1
queueMicrotask1
then3
set1
then2
then4
set2

简单图示如下

最后一道题,加上了 async、await
先来一个结论,通过async定义的函数在调用栈中执行,await 将异步程序变成同步,所以await后面执行的程序需要等到await定义的函数执行完成才执行,需要在微任务队列中等待
async function async1 () {
console.log('async1 start')
await async2();
console.log('async1 end')
} async function async2 () {
console.log('async2')
} console.log('script start') setTimeout(function () {
console.log('setTimeout')
}, 0) async1(); new Promise (function (resolve) {
console.log('promise1')
resolve();
}).then (function () {
console.log('promise2')
}) console.log('script end')

函数只有调用的时候才会推入调用栈中,所以最先执行的是 console.log,即输出 script start,然后setTimeout函数("setTimeout")放入宏任务队列中等待,调用async1函数,输出 async1 start,执行async2函数,输出async2,("async1 end")放入微任务队列中等待,继续向下执行Promise函数,输出 promise1,then函数中的("promise2")放入微任务队列中等待,输出 script end。

调用栈的程序都已执行完毕,此时开始执行微任务队列中的程序,依次输出 async1 end、promise2。
 
微任务队列中的程序也已执行完成,开始执行宏任务中的程序,输出setTimeout。
 
输出顺序为
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout

简单图示如下

 
判断执行顺序可以记住以下几个重点
1、promise中的回调函数立刻执行,then中的回调函数会推入微任务队列中,等待调用栈所有任务执行完才执行
2、async函数里的内容是放入调用栈执行的,await的下一行内容是放入微任务执行的
3、调用栈执行完成后,会不断的轮询微任务队列,即使先将宏任务推入队列,也会先执行微任务
 

javascript事件循环机制及面试题详解的更多相关文章

  1. 一篇文章图文并茂地带你轻松学完 JavaScript 事件循环机制(event loop)

    JavaScript 事件循环机制 (event loop) 本篇文章已经默认你有了基础的 ES6 和 javascript语法 知识. 本篇文章比较细致,如果已经对同步异步,单线程等概念比较熟悉的读 ...

  2. javaScript 事件循环机制

    JavaScript是单线程的编程语言,只能同一时间内做一件事.但是在遇到异步事件的时候,js线程并没有阻塞,还会继续执行,这就是因为JS有事件循环机制. 事件循环流程总结 主线程开始执行一段代码, ...

  3. javascript事件循环机制 浅尝手记

    引入 众所周知Javascript是一个单线程的机制,虽然可以依托多线程的浏览器实现页面如何实现页面复杂的渲染.事件响应,但仍不会改变其单线程的本质:所以对于js的事件循环机制的了解是一个前端人员的必 ...

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

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

  5. 深入浅出Javascript事件循环机制

    一.JS单线程.异步.同步概念 众所周知,JS是单线程(如果一个线程删DOM,一个线程增DOM,浏览器傻逼了-所以只能单着了),虽然有webworker酱紫的多线程出现,但也是在主线程的控制下.web ...

  6. 浏览器中的JavaScript事件循环机制

    浏览器的事件循环机制是HTML中定义的规范. JavaScript有一个主线程和调用栈,所有的任务都会被放到调用栈等待主线程执行. JS调用栈 是一种先进后出的数据结构.当函数被调用时,会被添加到栈中 ...

  7. JS JavaScript事件循环机制

    区分进程和线程 进程是cpu资源分配的最小单位(系统会给它分配内存) 不同的进程之间是可以同学的,如管道.FIFO(命名管道).消息队列 一个进程里有单个或多个线程 浏览器是多进程的,因为系统给它的进 ...

  8. JavaScript事件循环机制

    事件循环 事件循环不仅仅包含事件队列,而是具有至少两个队列,除了事件,还要保持浏览器执行的其他操作.这些操作被称为任务,并且分为两类:宏任务(或通常称为任务)和微任务. 单次循环迭代中,最多处理一个宏 ...

  9. 安卓中的消息循环机制Handler及Looper详解

    我们知道安卓中的UI线程不是线程安全的,我们不能在UI线程中进行耗时操作,通常我们的做法是开启一个子线程在子线程中处理耗时操作,但是安卓规定不允许在子线程中进行UI的更新操作,通常我们会通过Handl ...

  10. 一道面试题引发对javascript事件循环机制(Event Loop)的 思考(这里讨论针对浏览器)

随机推荐

  1. SSH客户端常用工具SecureCRT操作

    目录 1.1 SecureCRT工具介绍 1.2 SecureCRT工具安装 1.3配置SecureCRT连接Linux主机 1.4调整SecureCRT终端显示和回滚缓冲区大小 1.5调整字体及光标 ...

  2. AI 绘画基础 - 细数 Stable Diffusion 中的各种常用模型 【🧙 魔导士装备图鉴】

    AI 绘画新手魔导士在刚开始玩 Stable Diffusion 时总会遇到各种新的概念,让人困惑,其中就包括各种模型和他们之间的关系. 魔法师入门得先认识各种法师装备(各种模型),让我们遇到问题知道 ...

  3. flask接口参数校验 jsonschema 的使用

    开头 flask接口开发中参数校验可以用到的方法有很多,但是我比较喜欢跟前端的js检验类似,故选用到了 jsonschema 这个参数校验的库 Demo 下面是一个比较全的参数校验的接口,日后方便参考 ...

  4. 文心一言 VS chatgpt (6)-- 算法导论2.3 1~2题

    一.使用图 2-4作为模型,说明归并排序在数组 A=(3,41,52,26,38,57,9,49)上的操作. 文心一言: 使用图 2-4作为模型,说明归并排序在数组 A=(3,41,52,26,38, ...

  5. Matplotlib.pyplot.plot 绘图

    Matplotlib.pyplot 创建图形.在图形中创建创建一个绘图区域.在绘图区域中你那个绘制一些线.在图形中添加标签之类 画二维平面图 x = np.arange(0, 10, 2) y1 = ...

  6. Python基础 - 注释

    单行注释 Python中使用#表示单行注释.一般在#后面添加一个空格,再添加注释内容 1 # 这是单行注释 多行注释   Python中使用三个单引号或三个双引号表示多行注释. 1 ''' 2 这是使 ...

  7. Linux,会这些就够了

    在测试当中,其实对Linux的要求不高,我们在工作中需要记住常用的一些命令,不常用的实际用到的时候再查在记即可,最重要我们要使用命令可以查看日志,定位bug   目录篇: 可用  pwd  命令查看用 ...

  8. Intellij IDEA最新激活码,适合2022,2023和所有版本,永久更新

    分享一下 IntelliJ IDEA 2023.1 最新激活注册码,破解教程如下,可免费永久激活,亲测有效,下面是详细文档哦~ 申明:本教程 IntelliJ IDEA 破解补丁.激活码均收集于网络, ...

  9. 纠删码技术在vivo存储系统的演进【上篇】

    作者:vivo 互联网服务器团队- Gong Bing 本文将学术界和工业界的纠删码技术的核心研究成果进行了相应的梳理,然后针对公司线上存储系统的纠删码进行分析,结合互联网企业通用的IDC资源.服务器 ...

  10. RALB负载均衡算法的应用

    一.背景 搜索推荐算法架构为京东集团所有的搜索推荐业务提供服务,实时返回处理结果给上游.部门各子系统已经实现了基于CPU的自适应限流,但是Client端对Server端的调用依然是RR轮询的方式,没有 ...