js 异步、栈、事件循环、任务队列

在开发中经常遇到js的异步问题,为了方便理解,记录下来,随时回顾。

  • 以下的所有代码都是在浏览器环境下运行

在浏览器中js的运行是依赖浏览器js引擎来解析的,并且是在一定的runtime(运行时)的环境被调用,被执行。由于js引擎是单线程的,所以在执行dom渲染,script引入的时候这些操作是同步的,js引擎会通过 Event Loop 的机制,按顺序把任务放入栈中执行

而在代码中产生的异步代码则是由 runtime 提供的,拥有和Js引擎互不干扰的线程

栈是一个后进先出的一种数据结构,执行起来效率比较高,往往堆里存放着一些对象。而栈中则存放着一些基础类型变量以及对象的指针,在函数调用的时候,会产生函数的执行栈,也叫执行上下文,这个执行环境中存在着这个函数的私有作用域,上层作用域的指向,函数的参数,这个作用域中定义的变量以及这个作用域的this对象。 而当一系列函数被依次调用的时候,因为js是单线程的,同一时间只能执行一个函数,于是这些函数被排队在一个单独的地方。这个地方被称为执行栈。

当一个脚本第一次执行的时候,js引擎会解析这段代码,并将其中的同步代码按照执行顺序加入执行栈中,然后从头开始执行。如果当前执行的是一个方法,那么js会向执行栈中添加这个方法的执行环境,然后进入这个执行环境继续执行其中的代码。当这个执行环境中的代码 执行完毕并返回结果后,js会退出这个执行环境并把这个执行环境销毁,回到上一个方法的执行环境。。这个过程反复进行,直到执行栈中的代码全部执行完毕。

总的来说

  • 栈存放着一些基础类型变量以及对象的指针
  • 当代码执行的时候,同步代码按照执行顺序开始执行
  • 当代码执行的时候,碰到函数,引擎会在栈里产生这个函数执行栈,也叫执行上下文。
  • 当代码执行到函数的时候,会进入这个执行环境继续执行其中的代码,反复进行,全部执行完

任务队列

Js 中,有两类任务队列:宏任务队列(macro tasks)和微任务队列(micro tasks)。宏任务队列可以有多个,微任务队列只有一个
  • 宏任务:script(全局任务), setTimeout, setInterval, setImmediate, I/O, UI rendering.
  • 微任务:process.nextTick (node.js中进程相关的对象), Promise, Object.observer, MutationObserver。

在浏览器的 Event Loop机制中,整个流程可以用张图来表示一下:

这张图中可以看到:

  • 微任务队列(micro tasks)只会有一个
  • 宏任务队列(macro tasks)可以有多个
  • click ajax 等回调方法都会进入到宏任务队列(macro tasks)中,当然也包括上面的

而在浏览器的Event Loop机制运行时,宏任务队列(macro tasks)和微任务队列(micro tasks)的关系,关于这点详见(关系

  • 宏任务按顺序执行,且浏览器在每个宏任务之间渲染页面
  • 所有微任务也按顺序执行,且在以下场景会立即执行所有微任务
    • 每个回调之后且js执行栈中为空。
    • 每个宏任务结束后。

而在 NodeJs 的 Event Loop 遵循的是 libuv

这个库是node作者自己写的,内部实现了一整套的异步io机制(内部使用c++和js实现),使我们开发异步程序变得简单,因为这个原因导致了一些js解析和浏览器的会不一样。

NodeJs 的运行是这样的:

  • 初始化 Event Loop
  • 执行您的主代码。这里同样,遇到异步处理,就会分配给对应的队列。直到主代码执行完毕。
  • 执行主代码中出现的所有微任务:先执行完所有nextTick(),然后在执行其它所有微任务。
  • 开始 Event Loop

当每个阶段执行完毕后,都会执行所有微任务(先 nextTick,后其它),然后再进入下一个阶段。

最后我们来段代码彻底解析下两类任务队列在运行时的逻辑

console.log('1');

setTimeout(function() {
console.log('2');
process.nextTick(function() {
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
})
process.nextTick(function() {
console.log('6');
})
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
console.log('8')
}) setTimeout(function() {
console.log('9');
process.nextTick(function() {
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
})

结合上面讲的逻辑 可以分析一波得出最后答案是1,7,6,8,2,4,3,5,9,11,10,12

js的事件循环和任务队列的更多相关文章

  1. JS 的线程、事件循环、任务队列简介

    JS 是单线程的,但是却能执行异步任务,这主要是因为 JS 中存在事件循环(Event Loop)和任务队列(Task Queue). 事件循环:JS 会创建一个类似于 while (true) 的循 ...

  2. JS 事件循环机制 - 任务队列、web API、JS主线程的相互协同

    一.JS单线程.异步.同步概念 从上一篇说明vue nextTick的文章中,多次出现“事件循环”这个名词,简单说明了事件循环的步骤,以便理解nextTick的运行时机,这篇文章将更为详细的分析下事件 ...

  3. JS JavaScript事件循环机制

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

  4. JS-线程、事件循环、任务队列

    JS 是单线程的,但是却能执行异步任务,这主要是因为 JS 中存在事件循环(Event Loop)和任务队列(Task Queue). 事件循环: JS 会创建一个类似于 while (true) 的 ...

  5. 浏览器中 JS 的事件循环机制

    目录 事件循环机制 宏任务与微任务 实例分析 参考 1.事件循环机制 浏览器执行JS代码大致可以分为三个步骤,而这三个步骤的往复构成了JS的事件循环机制(如图). 第一步:主线程(JS引擎线程)中执行 ...

  6. js的事件循环绑定和jQuery的隐式迭代

    js的事件循环绑定和jQuery的隐式迭代 js事件循环绑定 jQuery隐式迭代 先举一个例子:给定一个ul,点击列表内的每一个li元素,使它的背景色变红,下边分别用js代码和jQuery实现. & ...

  7. Node.js:事件循环

    ylbtech-Node.js:事件循环 1.返回顶部 1. Node.js 事件循环 Node.js 是单进程单线程应用程序,但是通过事件和回调支持并发,所以性能非常高. Node.js 的每一个 ...

  8. Node.js 的事件循环机制

    目录 微任务 事件循环机制 setImmediate.setTimeout/setInterval 和 process.nextTick 执行时机对比 实例分析 参考 1.微任务 在谈论Node的事件 ...

  9. JS:事件循环机制、调用栈以及任务队列

    点击查看原文 写在前面 js里的事件循环机制十分有趣.从很多面试题也可以看出来,考察简单的setTimeout也就是考察这个机制的. 在之前,我只是简单地认为由于函数执行很快,setTimeout执行 ...

随机推荐

  1. Python数据类型-str,list常见操作

    一.字符串操作 语法:字符串名.startwith('字符串') 功能:判断字符串里是否以xxx开头 范例: 扩展:从控制台接收输入居住地址,如果地址以北京市开头,则输出北京人口,否则输入非北京人口. ...

  2. jQuery与javascript

    jQuery 是一个 JavaScript 库,jQuery 极大地简化了 JavaScript 编程. javaScript(js)和jQuery(jq) 都是找元素.操作元素 Dom操作的区别: ...

  3. MacOS SVN简单入门

    背景:MacOS内置了SVN的客户端和服务器端的软件,下边所使用到的目录需要结合自己电脑的具体情况进行设置,并不是很困难. MacOS SVN简单入门 第一部分,创建本地的SVN测试仓库,并修改相应的 ...

  4. bzoj 2125 最短路 点双 圆方树

    LINK:最短路 一张仙人掌图 求图中两点最短路. \(n<=10000,Q<=10000,w>=1\) 考虑边数是多少 m>=n-1 对于一张仙人掌图 考虑先构建出来dfs树 ...

  5. 每日一道 LeetCode (5):最长公共前缀

    前文合集 每日一道 LeetCode 前文合集 代码仓库 GitHub: https://github.com/meteor1993/LeetCode Gitee: https://gitee.com ...

  6. AbstractRoutingDataSource 实现动态数据源切换原理简单分析

    AbstractRoutingDataSource 实现动态数据源切换原理简单分析 写在前面,项目中用到了动态数据源切换,记录一下其运行机制. 代码展示 下面列出一些关键代码,后续分析会用到 数据配置 ...

  7. GLIBC_2.18 not found 之类的问题解决办法

    先到 /usr/lib64 下 ll libstdc++* 会发现 libstdc++.so.6 指向了一个新的.比如我这里之前指向6.0.22这个版本,报错 此时只要重新建立软连接 将软连接指向旧的 ...

  8. Redis服务之常用数据类型

    上一篇博客我们聊了下redis的主从复制.aof持久化.集群.慢日志相关配置指令的说明,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/13416534.html ...

  9. java进阶(3)--接口

    一.基本概念 1.接口为引用数据类型,编译后也是class字节码文件 2.接口是完全抽象的,(抽象类是半抽象的),属于特殊的抽象类 3.接口定义方法:[修饰符列表]interface 接口名{} 4. ...

  10. 我是键盘侠-键盘流神器Vimium

    黑客的浏览器. Vimium本着Vim的精神为导航和控制提供键盘快捷键. 注意:谷歌不允许 Vimium在 Chrome Web Store页面和 新选项卡页面上运行.所以按键无效不要惊讶 Vimiu ...