简评:如果你对 JavaScript 异步的原理感兴趣,这里有一篇不错的介绍。

JavaScript 同步代码是如果工作的

在介绍 JavaScript 异步执行之前先来了解一下, JavaScript 同步代码是如何执行的。

这里有两个概念需要了解:

** 执行上下文(Excution Context)**

执行上下文是一个抽象的概念,用于表示 JavaScript 的运行环境,任何代码都会有一个执行上下文。

全局代码运行在全局执行上下文,函数里的代码运行在函数执行上下文,每一个函数都有自己的执行上下文。

调用堆栈(Call Stack)

调用栈是一个具有 LIFO(后进先出)结构的栈,用于储存代码执行阶段所有的执行上下文。

因为 JavaScript 是单线程的,所以 JavaScript 只有一个单独的调用栈。

我们以下面例子介绍同步代码执行过程。

const second = () => {
console.log('Hello there!');
}
const first = () => {
console.log('Hi there!');
second();
console.log('The End');
}
first();

创建全局上下文(由 main() 表示),并将全局上下文推到栈顶。然后依次将遇到函数执行上下文推到栈顶(如果函数中执行其他他函数,其他函数依次推到栈顶以此类推)。当函数执行完毕对应的执行上下文会从调用栈弹出,程序结束时全局上下文从调用栈弹出。

JavaScript 异步代码是如何执行的?

通过上个章节我们已经对调用栈和 JavaScript 的同步执行有了基本的了解,现在来看看 JavaScript 异步执行是如何工作的。

什么是阻塞?

由于 JavaScript 是单线程的,如果某个函数耗费的时间比较长,会阻塞后面的任务执行,这就造成了阻塞。解决阻塞最简单的方法是函数直接返回不等待,使用异步回调来处理返回结果。

在了解 JavaScript 异步执行之前还需要知道一些概念,事件循环和回调队列(也称为任务队列或消息队列)。

注意:Event Loop 、Web APIs 和 Message Queue 并不是 JavaScript 引擎的一部分,而是浏览器运行时环境和 Nodejs 运行时环境的一部分。

我们以下面代码为例,解释异步代码是如何执行的。

const networkRequest =()=> {
setTimeout(()=> {
console.log('Async Code');
},2000);
};
console.log('Hello World');
networkRequest();
console.log('The End');

当上述程序加载到浏览器时 console.log(‘Hello World’) 代码执行时会一次在调用栈推入和弹出。遇到 networkRequest() 将其推入到调用栈顶。然后继续将 networkRequest 内的 setTimeout 方法推入栈顶,随后 setTimeout networkRequest 依次出栈。最后对 console.log(‘The End’) 进行入栈出栈。

当 timer 到期后会将 callback 推入 message queue(消息队列)中,此时 callback 不会马上执行。会等待事件循环调度。

事件循环

事件循环的作用是查看调用栈并确定调用栈是否空闲。如果调用栈空闲,even loop 会查看消息队列是否有待处理的 callback 需要触发。例子中的消息队列只包含一个 callback,当调用栈为空的时候,even loop 会将 callback 推入调用栈中触发 networkRequest 的回调。

DOM 事件

消息队列还会包含来自 DOM 的事件回调,比如鼠标和键盘事件回调。例如:

document.querySelector('.btn').addEventListener('click',function callback(event) {
console.log('Button Clicked');
});

对于 DOM 事件,当具体的事件触发会将 callback 推入消息队列中,等待 even loop 来调度执行。

ES6 job queue/micro-task queue

ES6 新增了 job queue/micro-task queue 概念,在 Promise 中用到。job queue 比 message queue 拥有更高的优先级。意味着 job queue 和 message queue 都有任务时会优先执行 job queue 中的任务。例如:

console.log('Script start');

// callback 在 message queue 中
setTimeout(function callback() {
console.log('setTimeout');
}, 0); // 任务在 micro-task queue 中
new Promise((resolve, reject) => {
resolve('Promise resolved');
}).then(res => console.log(res))
.catch(err => console.log(err));
console.log('Script End'); // 输出:
Script start
Script End
Promise resolved
setTimeout

再来看下一个例子(两个 setTimeout 和 两个 Promise):

console.log('Script start');
setTimeout(() => {
console.log('setTimeout 1');
}, 0);
setTimeout(() => {
console.log('setTimeout 2');
}, 0);
new Promise((resolve, reject) => {
resolve('Promise 1 resolved');
}).then(res => console.log(res))
.catch(err => console.log(err));
new Promise((resolve, reject) => {
resolve('Promise 2 resolved');
}).then(res => console.log(res))
.catch(err => console.log(err));
console.log('Script End'); //输出为
Script start
Script End
Promise 1 resolved
Promise 2 resolved
setTimeout 1
setTimeout 2

由此可见 micro-task queue 中的所有任务都会优先于 message queue 中的任务执行。

原文:Understanding Asynchronous JavaScript — the Event Loop

异步 JavaScript - 事件循环的更多相关文章

  1. c#封装DBHelper类 c# 图片加水印 (摘)C#生成随机数的三种方法 使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象 c# 制作正方形图片 JavaScript 事件循环及异步原理(完全指北)

    c#封装DBHelper类   public enum EffentNextType { /// <summary> /// 对其他语句无任何影响 /// </summary> ...

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

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

  3. JavaScript 事件循环

    JavaScript 事件循环 事件循环 任务队列 async/await 又是如何处理的呢 ? 定时器问题 阻塞还是非阻塞 实际应用案例 拆分 CPU 过载任务 进度指示 在事件之后做一些事情 事件 ...

  4. JavaScript:彻底理解同步、异步和事件循环(Event Loop) (转)

    原文出处:https://segmentfault.com/a/1190000004322358 一. 单线程 我们常说"JavaScript是单线程的". 所谓单线程,是指在JS ...

  5. JavaScript:彻底理解同步、异步和事件循环(Event Loop)

    一. 单线程 我们常说"JavaScript是单线程的". 所谓单线程,是指在JS引擎中负责解释和执行JavaScript代码的线程只有一个.不妨叫它主线程. 但是实际上还存在其他 ...

  6. 让你高效的理解JavaScript中的同步、异步和事件循环

    "同步请求","异步请求"相信这两词在程序猿的世界中频频出现,到底是词性的妖娆,还是撸代码的基础要求,下面直接分享本人学习的好东西,保证让你深入浅出,爽得不要不 ...

  7. [转] JavaScript:彻底理解同步、异步和事件循环(Event Loop)

    一. 单线程 我们常说“JavaScript是单线程的”. 所谓单线程,是指在JS引擎中负责解释和执行JavaScript代码的线程只有一个.不妨叫它主线程. 但是实际上还存在其他的线程.例如:处理A ...

  8. JavaScript 事件循环及异步原理(完全指北)

    引言 最近面试被问到,JS 既然是单线程的,为什么可以执行异步操作? 当时脑子蒙了,思维一直被困在 单线程 这个问题上,一直在思考单线程为什么可以额外运行任务,其实在我很早以前写的博客里面有写相关的内 ...

  9. 总结:JavaScript异步、事件循环与消息队列、微任务与宏任务

    本人正在努力学习前端,内容仅供参考.由于各种原因(不喜欢博客园的UI),大家可以移步我的github阅读体验更佳:传送门,喜欢就点个star咯,或者我的博客:https://blog.tangzhen ...

随机推荐

  1. mysql常用语句及关键字

    一.常用sql语句 1.创建数据库userCREATE  DATABASE user; 2.删除数据库userDROP DATABASE user; 3.使用数据库userUSE user;显示数据库 ...

  2. 部署MVC项目ManagedPipelineHandler报错

    "处理程序ExtensionlessUrlHandler-Integrated-4.0在其模块列表中有一个错误模块ManagedPipelineHandler": 解决方法:以管理 ...

  3. Java解析XML文档——dom解析xml

    一.前言 用Java解析XML文档,最常用的有两种方法:使用基于事件的XML简单API(Simple API for XML)称为SAX和基于树和节点的文档对象模型(Document Object M ...

  4. java 对象直接序列化

    序列化类,加字段后, 使用该类从现有文件反序列化时,以前字段可以正常读出 但是反序列化后,那些未对应字段会被设置成null ,即使在类的定义种已设置了初始值 --------------------- ...

  5. UVa 247 Calling Circles (DFS+Floyd)

    题意:如果两个人互通电话,那么他们就在一个电话圈里,现在给定 n 个人,并且给定 m 个通话记录,让你输出所有的电话圈. 析:刚开始没想到是Floyd算法,后来才知道是这个算法,利用这个算法进行连通性 ...

  6. 异步IO原理及相应函数

    何为异步IO? (1)几乎可以认为:异步IO就是操作系统用软件实现的一套中断响应系统.(2)异步IO的工作方法是:我们当前进程注册一个异步IO事件(使用signal注册一个信号 SIGIO的处理函数) ...

  7. Javascript设计模式理论与实战:桥接模式

    桥接模式将抽象部分与实现部分分离开来,使两者都可以独立的变化,并且可以一起和谐地工作.抽象部分和实现部分都可以独立的变化而不会互相影响,降低了代码的耦合性,提高了代码的扩展性. 基本理论 桥接模式定义 ...

  8. 使用google chrome抓取数据:抓取全国的高中的数据

    http://tomycat.github.io/blog/other/2014/05/28/use-google-chrome-capture-data.html

  9. Solr相似度名词:VSM(Vector Space Model)向量空间模型

    最近想学习下Lucene ,以前运行的Demo就感觉很神奇,什么原理呢,尤其是查找相似度最高的.最优的结果.索性就直接跳到这个问题看,很多资料都提到了VSM(Vector Space Model)即向 ...

  10. 使用jdbc的方式访问kylin cube的数据

    使用jdbc的方式访问kylin cube的数据 引用kylin相关的jar包 <dependency> <groupId>org.apache.kylin</group ...