JavaScript 的核心机制——event loop(最易懂版)
前言
javascript从诞生之日起就是一门单线程的非阻塞的脚本语言。
非阻塞就是当代码需要进行一项异步任务(无法立刻返回结果,需要花一定时间才能返回的任务,如ajax事件)时,主线程会挂起(pending)这个任务,然后在异步任务返回结果的时候再根据一定规则去执行相应的回调。
javascript引擎到底是如何实现的这一点呢?
1.执行栈
存放当前正在执行的代码的地方被称为执行栈。
当我们调用一个方法的时候,js会生成一个与这个方法对应的执行环境(context),又叫执行上下文。这个context存放着这个方法的私有作用域,上层作用域的指向,方法的参数,这个作用域中定义的变量以及这个作用域的this对象。
- 当一个脚本第一次执行的时候,js引擎会解析这段代码,并将其中的同步代码按照执行顺序加入栈中,然后从头开始执行。
- 如果当前执行的是一个方法,那么js会向执行栈中添加这个方法的context,然后进入这个context继续执行其中的代码。
- 当这个context中的代码 执行完毕并返回结果后,js会退出这个context并把这个context销毁,回到上一个方法的context。(函数的相互调用,就像一个栈!)
- 这个过程反复进行,直到执行栈中的代码全部执行完毕。
这个过程可以是无限进行下去的,除非发生了栈溢出,即超过了所能使用内存的最大值。
2.事件循环队列
- 执行栈遇到一个异步任务后(如ajax请求)并不会一直等待其执行完毕,而是继续执行 栈中的后续任务,这就是js的异步非阻塞特性。
- 当一个异步事件完成后,js会将这个事件的回调任务放入事件队列中,而不会影响当前的执行栈。
- 当前执行栈中的所有任务都执行完毕时,事件队列中如果有任务的话,主线程会从中取出最优先的任务,放入执行栈中执行。
- 如此反复,每次执行栈执行完毕,就去任务队列中查找,这样就形成了一个无限的循环;这就是所谓的 event loop 。
- 注意:当所有任务都执行完了(事件队列中也没有任务了),js的运行也不会停止。js宿主(浏览器)每一帧都会执行一次上述的过程,从而保证异步回调会快速响应。可以参见浏览器的requestIdleCallback 或 requestAnimationFrame !
这就是“事件循环(Event Loop)”
经典示例:
setTimeout(() => {
console.log('settimeout')
}, 0)
let start = Date.now();
while (Date.now() - start < 2000);
console.log('finished');
/**
* 运行上面代码,并理解这个过程:
* 执行栈中同步任务先运行2s的循环,然后输出 finished
* 然后取出异步任务执行,输出settimeout
*/
3.异步任务的顺序
不同的异步任务是放在不同的队列里的,当多个队列同时存在待处理的任务时,异步任务之间的执行优先级也有区别!
总体有两种异步任务—— 微观任务(micro task)和宏观任务(macro task)。微观任务总是先于宏观任务被执行!
微观任务是 js引擎自己发出的异步任务,如Promise。 es2015之前,js引擎是无法自己创建异步任务的,所以微观任务是es2015才开始有的概念。
宏观任务是 宿主发起的异步任务,如setTimeout,XMLHttpRequest等 都是宿主window上的对象。
回到上一节,事件循环的第3点:主线程会去查找事件队列是否有任务! 是先取微观任务队列中的任务,直到微观任务队列为空,再找宏观任务队列。
经典示例:
setTimeout(() => {
console.log('settimeout') //宏观异步任务,最后输出
}, 0)
Promise.resolve().then(()=>{
console.log('promise') //微观异步任务,第二个输出
})
console.log('finished') // 同步的,最先输出
异步任务优先级 总体上是这样的顺序,如果要更细节的优先级呢?
微观任务优先级
微观任务目前只有一种,就是Promise创建的,而且微观任务队列只有一个,再加上js是单线程的,即便有多种也很好理解。
Promise.resolve().then(() => {
Promise.resolve().then(() => {
Promise.resolve().then(() => {
console.log('1-1-1')
});
console.log('1-1')
});
console.log('1')
});
Promise.resolve().then(() => {
Promise.resolve().then(() => {
console.log('2-1')
});
console.log('2')
});
// 依次输出: 1 2 1-1 2-1 1-1-1
宏观任务优先级
宏观任务有多种,而且是宿主发起(通常是多线程)又该如何确定顺序呢?
setTimeout(() => {
console.log('timmer')
}, 0);
let start = Date.now();
while (Date.now() - start < 5000);
function clickDiv(){ //点击按钮时触发
console.log('click')
}
上面代码很有意思,主线程的同步代码执行需要5s,如果用户在第2s点击了按钮,那么5s后 是先输出 click,还是先输出 timmer 呢?
timmer事件和UI事件,是不同的事件队列,尽管他们都是宏观任务。他们的优先级交给浏览器来决定的 —— 具体宏观任务的优先级,是由具体宿主自己决定的!
所以这个问题请参考具体的宿主资料吧(nodejs 和 浏览器)
JavaScript 的核心机制——event loop(最易懂版)的更多相关文章
- 一篇文章图文并茂地带你轻松学完 JavaScript 事件循环机制(event loop)
JavaScript 事件循环机制 (event loop) 本篇文章已经默认你有了基础的 ES6 和 javascript语法 知识. 本篇文章比较细致,如果已经对同步异步,单线程等概念比较熟悉的读 ...
- javascript的执行机制—Event Loop
既然今天要谈的是javascript的事件循环机制,要理解事件循环,首先要知道事件循环是什么. 我们先从一个例子来看一下javascript的执行顺序. <script> setTimeo ...
- JavaScript Concurrency model and Event Loop 并发模型和事件循环机制
原文地址:https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop JavaScript 有一个基于 event loop 的 ...
- Js 运行机制 event loop
Js - 运行机制 (Even Loop) Javascript 的单线程 - 引用思否的说法: JavaScript的一个语言特性(也是这门语言的核心)就是单线程.什么是单线程呢?简单地说就是同一时 ...
- javascript基础修炼(5)—Event Loop(Node.js)
开发者的javascript造诣取决于对[动态]和[异步]这两个词的理解水平. 一. 一道考察异步知识的面试题 题目是这样的,要求写出下面代码的输出: setTimeout(() => { co ...
- JavaScript并发模型与Event Loop (转载)
并发模型可视化描述 model.svg 如上图所示,Javascript执行引擎的主线程运行的时候,产生堆(heap)和栈(stack),程序中代码依次进入栈中等待执行, 若执行时遇到异步方法,该异步 ...
- js事件循环机制(Event Loop)
javascript从诞生之日起就是一门 单线程的 非阻塞的 脚本语言,单线程意味着,javascript代码在执行的任何时候,都只有一个主线程来处理所有的任务,非阻塞靠的就是 event lo ...
- js 彻底搞懂事件循环机制 Event Loop
我们都知道javascript是单线程语言,就是因为单线程的特性,就不得不提js中的同步和异步 一.同步和异步 所谓单线程,无非就是同步队列和异步队列,js代码是自上向下执行的,在主线程中立即执行的就 ...
- JavaScript 运行机制 (Event Loop)
单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务.如果前一个任务耗时很长,后一个任务就不得不一直等着. 所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步 ...
随机推荐
- 【draft】Team project :Bing dictionary plug-in
课后~ 开会调研开会调研开会~ 在和Bing词典负责人进行了可行性的深入磋商后,我们对本次选题有了更加清晰的认识~困难好多~然而终于敲定了项目内容,我们的目标是这样一款神奇的插件,它帮你记录下新近查询 ...
- Java Instrumentation插桩技术学习
Instrumentation基础 openrasp中用到了Instrumentation技术,它的最大作用,就是类的动态改变和操作. 使用Instrumentation实际上也可以可以开发一个代理来 ...
- Spring Boot将Mybatis返回结果转为驼峰的三种实现方式
本文不再更新,可能存在内容过时的情况,实时更新请访问原地址:Spring Boot将Mybatis返回结果转为驼峰的三种实现方式: 我们通常获取Mybatis返回的数据结果时想要将字段以驼峰的形式返回 ...
- mongodb connection refused because too many open connections: 819
Env Debian 9 # 使用通用二进制方式安装 # mongod --version db version v3.4.21-2.19 git version: 2e0631f5e0d868dd5 ...
- python小白入门之导入指定的模块
在python中导入模块是通过关键字import进行导入的,下面演示一下,模块的导入,指定模块别名,指定函数别名,调用模块中所有的函数运行结果: 1.模块的导入Study.py文件里面的内容是:形式 ...
- 2019-2020-1 20199308《Linux内核原理与分析》第八周作业
<Linux内核分析> 第七章 可执行程序工作原理 7.1 知识点 1.目标文件:编译器生成的文件,"目标"指平台,它决定了编译器使用的机器指令集. 2.目标文件格式: ...
- 数据包的抓取[tcpdump]的应用
[root@server ~]# yum install tcpdump [root@server ~]# yum install wireshark 1.默认情况下,直接启动tcpdump将监视第一 ...
- Display a QMessageBox from a QThread
Emit a signal. Since you cannot do UI stuff in a Qthread, instead send your message as an argument o ...
- 3年前的一个小项目经验,分享给菜鸟兄弟们(公文收发小软件:小技能 SmallDatetime)...
为什么80%的码农都做不了架构师?>>> 这个系统中的数据库有100多M,里面当然有很多表,我的每个表里,有几个字段,都是一样的例如 CreateUserID.CreateDat ...
- 写给Java程序员的Java虚拟机学习指南
大家好,我是极客时间<深入拆解Java虚拟机>作者.Oracle Labs高级研究员郑雨迪.有幸借这个专题的机会,能和大家分享为何Java工程师要学Java虚拟机?如何掌握Java虚拟机? ...