资讯专栏INFORMATION COLUMN

异步 JavaScript - 事件循环

tolerious / 1438人阅读

摘要:创建全局上下文由表示,并将全局上下文推到栈顶。在了解异步执行之前还需要知道一些概念,事件循环和回调队列也称为任务队列或消息队列。会等待事件循环调度。事件循环事件循环的作用是查看调用栈并确定调用栈是否空闲。

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/99753.html

相关文章

  • 总结javascript基础概念(二):事件队列循环

    摘要:而事件循环是主线程中执行栈里的代码执行完毕之后,才开始执行的。由此产生的异步事件执行会作为任务队列挂在当前循环的末尾执行。在下,观察者基于监听事件的完成情况在下基于多线程创建。 主要问题: 1、JS引擎是单线程,如何完成事件循环的? 2、定时器函数为什么计时不准确? 3、回调与异步,有什么联系和不同? 4、ES6的事件循环有什么变化?Node中呢? 5、异步控制有什么难点?有什么解决方...

    zhkai 评论0 收藏0
  • JavaScript单线程事件循环(Event Loop)那些事

    摘要:概述本篇主要介绍的运行机制单线程事件循环结论先在中利用运行至完成和非阻塞完成单线程下异步任务的处理就是先处理主模块主线程上的同步任务再处理异步任务异步任务使用事件循环机制完成调度涉及的内容有单线程事件循环同步执行异步执行定时器的事件循环开始 1.概述 本篇主要介绍JavaScript的运行机制:单线程事件循环(Event Loop). 结论先: 在JavaScript中, 利用运行至...

    Shisui 评论0 收藏0
  • JavaScript 运行机制详解(理解同步、异步事件循环)

    摘要:从异步过程的角度看,函数就是异步过程的发起函数,事件监听函数就是异步过程的回调函数。事件触发时,表示异步任务完成,会将事件监听器函数封装成一条消息放到消息队列中,等待主线程执行。 1.为什么JavaScript是单线程? JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊。JavaScrip...

    loonggg 评论0 收藏0
  • (转)JavaScript:同步、异步事件循环

    摘要:事件循环事件循环是指主线程重复从消息队列中取消息执行的过程。事件触发时,表示异步任务完成,会将事件监听器函数封装成一条消息放到消息队列中,等待主线程执行。 一. 单线程 我们常说JavaScript是单线程的。 所谓单线程,是指在JS引擎中负责解释和执行JavaScript代码的线程只有一个。不妨叫它主线程。 但是实际上还存在其他的线程。例如:处理AJAX请求的线程、处理DOM事件的线...

    android_c 评论0 收藏0
  • JavaScript运行机制和事件循环

    摘要:主线程不断重复上面的三步,此过程也就是常说的事件循环。所以主线程代码执行时间过长,会阻塞事件循环的执行。参考资料这一次,彻底弄懂执行机制任务队列的顺序机制事件循环搞懂异步事件轮询与中的事件循环 1. 说明 读过本文章后,您能知道: JavaScript代码在浏览器中的执行机制和事件循环 面试中经常遇到的代码输出顺序问题 首先通过一段代码来验证你是否了解代码输出顺序,如果你不知道输出...

    Ververica 评论0 收藏0
  • 【笔记】 你不知道的JS读书笔记——异步

    摘要:异步请求线程在在连接后是通过浏览器新开一个线程请求将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件循环队列中。 基础:浏览器 -- 多进程,每个tab页独立一个浏览器渲染进程(浏览器内核) 每个浏览器渲染进程是多线程的,主要包括:GUI渲染线程 JS引擎线程 也称为JS内核,负责处理Javascript脚本程序。(例如V8引擎) JS引擎线程负...

    junnplus 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<