博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
异步 JavaScript - 事件循环
阅读量:6966 次
发布时间:2019-06-27

本文共 2991 字,大约阅读时间需要 9 分钟。

简评:如果你对 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 startScript EndPromise resolvedsetTimeout

再来看下一个例子(两个 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 startScript EndPromise 1 resolvedPromise 2 resolvedsetTimeout 1setTimeout 2

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

原文:

转载地址:http://jpbsl.baihongyu.com/

你可能感兴趣的文章
Apache 配置关闭文件目录浏览
查看>>
mybaits if判断进入不了
查看>>
Web前端开发人员和设计师必读文章推荐【系列九】
查看>>
几种常见的模式识别算法
查看>>
CI框架表单提交数据接收乱码
查看>>
7-14
查看>>
memcached原理详述及配置
查看>>
Requested bean is currently in creation: Is there an unresolvable circular reference?
查看>>
SQL Loader 的使用详解
查看>>
OAF打开新的窗口
查看>>
Object-C 如何把一个时间戳转换为一个标准的时间格式?
查看>>
在window和linux上通用的SprtLock类头实现文件
查看>>
文字在div中垂直居中
查看>>
C++中四种类型装换
查看>>
[iOS Animation]-CALayer 图层几何学
查看>>
设置oracle服务自动启动
查看>>
Linux 学习日记 2: 目录结构和文件操作
查看>>
AndEngine引擎学习之绘制直线
查看>>
TCP_Wrappers 基于TCP的安全控制
查看>>
android开机启动代码
查看>>