异步 JavaScript - 事件循环

OS_Expert
• 阅读 1087
简评:如果你对 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();

异步 JavaScript - 事件循环

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

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

通过上个章节我们已经对调用栈和 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');

异步 JavaScript - 事件循环

当上述程序加载到浏览器时 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
点赞
收藏
评论区
推荐文章
Souleigh ✨ Souleigh ✨
4年前
JavaScript 和 Node.js 中事件循环
1.JavaScript中事件循环可以参考《JavaScript忍者秘籍第二版》第十三章,讲解的很好。JavaScript中事件循环,主要就在理解宏任务和微任务这两种异步任务。宏任务(macrotask):setTimeOut、setInterval、setImmediate、I/O、各种callback、UI渲染、messageCh
Souleigh ✨ Souleigh ✨
4年前
理解 Javascript 中的 Async / Await
在本文中,我们将探讨async/await,对于每个Javascript开发人员来说,是异步编程的首选工具。如果您不熟悉javascript,请不要担心,本文将帮助您async/await从头开始理解。介绍async/await是javascript中的一种模式,可使您的代码以同步方式执行,但又不影响javascript的异步行为。定义异步功能要定义一
皮卡皮卡皮 皮卡皮卡皮
4年前
ajax
ajax定义:异步的JavaScript和XML是一种综合技术:运用了XMLHTTPRequest(xhr)和服务器交换数据,通过JavaScript局部渲染页面,从而实现异步的局部更新同步与异步同步代码按顺序执行,会阻塞代码执行(alert)异步不会阻塞代码XMLHTTPRequestxhrjsvarxhrnew
Wesley13 Wesley13
4年前
java第五周
AJAX的工作原理及其工作原理:1.定义及工作原理  AJAXAsynchronousJavaScriptandXML(异步的JavaScript和XML)。AJAX不是新的编程语言,而是一种使用现有标准的新方法。AJAX不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行。  AJAX是一种用于
菜园前端 菜园前端
2年前
什么是JavaScript异步模式
什么是异步模式?不会等待当前任务执行完毕,才会去执行下一个任务,这就是异步模式(Asynchronous)。开启异步后,就会跳过本任务,开始执行下一个任务,后续的逻辑一般会通过回调函数的方式定义。异步模式执行中,涉及到调用栈(Callstack)、消息队列
Stella981 Stella981
4年前
Javascript解析机制 执行机制
HTML5学堂:在学习JavaScript过程中,我们需要了解事件的机制是怎么执行的?本文将会提到JavaScript事件机制的解析,希望对大家有帮助!javascript解析的过程主要分为两个阶段,分别是编译与执行阶段。在编译期,javascript解释器将完成对javascript代码的预处理,即将javascript代码转换为字节码。在执行
Stella981 Stella981
4年前
JavaScript:再谈Tasks和Microtasks
JavaScript是单线程,也就是说JS的堆栈中只允许有一类任务在执行,不可以同时执行多类任务。在读js文件时,所有的同步任务是一条task,当然了,每一条task都是一个队列,按顺序执行。而如果在中途遇到了setTimeout这种异步任务,就会将它挂起,放到任务队列中去执行,等执行完毕后,如果有callback,就把callback推入到Tasks中去,
Stella981 Stella981
4年前
AJAX与Django
AJAX什么是AJAX?AJAX不是JavaScript的规范,它的缩写:AsynchronousJavaScriptandXML,意思就是用JavaScript执行异步网络请求。提交任务之后,不原地等待,直接执行下一行代码,任务的返回通过回调机制。局部刷新,不整体刷新,而是界面莫个地方局部刷新AJAX原理
Stella981 Stella981
4年前
JavaScript同步、异步及事件循环
同步、异步JS是单线程的,每次只能做一件事情。像以下这种情况,代码会按顺序执行,这个就叫同步。console.log(1);console.log(2);console.log(3);以下代码会输出2、3、1,像这种不按顺序执行的,或者说代码执行中间有时间间隙的,叫异步。setTimeout((
Stella981 Stella981
4年前
JavaScript 事件循环机制
javascript是一门单线程的非阻塞的脚本语言。单线程意味着javascript在执行代码的任何时候,都只有一个主线程来处理所有的任务。那么javascript引擎是如何实现这一点的呢?因为事件循环(eventloop)。先上图:!event_loop(https://oscimg.oschina.net/oscnet/up6
Stella981 Stella981
4年前
JavaScript 异步编程
❝掌握JavaScript主流的异步任务处理(本篇文章内容输出来源:《拉钩教育大前端训练营》参阅《你不知道的JavaScript中卷》异步章节)❞JavaScrip采用单线程模式工作的原因,需要进行DOM操作,如果多个线程同时修改DOM浏览器无法知道以哪个线程为主。JavaScirpt分为:同步模式、异步模式同步