深入剖析JavaScript异步之事件轮询

本篇文章给大家带来的内容是关于深入剖析javsscript异步之事件轮询,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

JavsScript 是一门单线程的编程语言,这就意味着一个时间里只能处理一件事,也就是说 JavaScript 引擎一次只能在一个线程里处理一条语句。

虽然单线程简化了编程代码,因为你不必太担心并发引出的问题,这也意味着你将在阻塞主线程的情况下执行长时间的操作,如网络请求。

想象一下从API请求一些数据,根据具体的情况,服务器需要一些时间来处理请求,同时阻塞主线程,使网页长时间处于无响应的状态。

这就是引入异步 JavaScript 的原因。使用异步 JavaScript(如 回调函数、promise、async/await),可以不用阻塞主线程的情况下长时间执行网络请求 :)

立即学习“Java免费学习笔记(深入)”;

可能你知道不知道 异步 JavsScript 是如何工作,并不要紧,但知道它是如何工作,对 JavaScript 异步更深入的了解是有帮助的。

所以不在啰嗦了,我们开始吧 :)

同步JavaScript是如何工作的?

在深入研究异步JavaScript之前,让我们首先了解同步 JavaScript 代码如何在 JavaScript 引擎中执行。例如:

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

登录后复制

要理解上述代码如何在 JavaScript 引擎中执行,我们必须理解执行上下文和调用堆栈(也称为执行堆栈)的概念。

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

调用栈

调用堆栈顾名思义是一个具有LIFO(后进先出)结构的堆栈,用于存储在代码执行期间创建的所有执行上下文。

JavaScript 只有一个调用栈,因为它是一种单线程编程语言。调用堆栈具有 LIFO 结构,这意味着项目只能从堆栈顶部添加或删除。

让我们回到上面的代码片段,并尝试理解代码如何在JavaScript引擎中执行。

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

登录后复制

3505796205-5bf79b3adf376_articlex.png

这里发生了什么?

当执行此代码时,将创建一个全局执行上下文(由main()表示)并将其推到调用堆栈的顶部。当遇到对first()的调用时,它会被推送到堆栈的顶部。

接下来,console.log(‘Hi there!’)被推送到堆栈的顶部,当它完成时,它会从堆栈中弹出。之后,我们调用second(),因此second()函数被推到堆栈的顶部。

console.log(‘Hello there!’)被推送到堆栈顶部,并在完成时弹出堆栈。second() 函数结束,因此它从堆栈中弹出。

console.log(“the End”)被推到堆栈的顶部,并在完成时删除。之后,first()函数完成,因此从堆栈中删除它。

程序在这一点上完成了它的执行,所以全局执行上下文(main())从堆栈中弹出。

异步JavaScript是如何工作的?

现在我们已经对调用堆栈和同步JavaScript的工作原理有了基本的了解,让我们回到异步JavaScript。

阻塞是什么?

让我们假设我们正在以同步的方式进行图像处理或网络请求。例如:

const processImage = (image) => {  /**  * doing some operations on image  **/  console.log('Image processed');}const networkRequest = (url) => {  /**  * requesting network resource  **/  return someData;}const greeting = () => {  console.log('Hello World');}processImage(logo.jpg);networkRequest('www.somerandomurl.com');greeting();

登录后复制

做图像处理和网络请求需要时间,当processImage()函数被调用时,它会根据图像的大小花费一些时间。

processImage() 函数完成后,将从堆栈中删除它。然后调用 networkRequest() 函数并将其推入堆栈。同样,它也需要一些时间来完成执行。

最后,当networkRequest()函数完成时,调用greeting()函数,因为它只包含一个控制台。日志语句和控制台。日志语句通常很快,因此greeting()函数立即执行并返回。

因此,我们必须等待函数(如processImage()或networkRequest())完成。这意味着这些函数阻塞了调用堆栈或主线程。因此,在执行上述代码时,我们不能执行任何其他操作,这是不理想的。

那么解决办法是什么呢?

最简单的解决方案是异步回调。我们使用异步回调使代码非阻塞。例如:

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

登录后复制

这里我使用了setTimeout方法来模拟网络请求。请记住setTimeout不是JavaScript引擎的一部分,它是web api(在浏览器中)和C/ c++ api(在node.js中)的一部分。

为了理解这段代码是如何执行的,我们必须理解更多的概念,比如事件轮询和回调队列(或消息队列)。

380725970-5bf7a056982d9_articlex.png

事件轮询、web api和消息队列不是JavaScript引擎的一部分,而是浏览器的JavaScript运行时环境或Nodejs JavaScript运行时环境的一部分(对于Nodejs)。在Nodejs中,web api被c/c++ api所替代。

现在让我们回到上面的代码,看看它是如何异步执行的。

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

登录后复制

1323757194-5bf7a5ea7065d_articlex.gif

当上述代码在浏览器中加载时,console.log(‘ Hello World ‘) 被推送到堆栈中,并在完成后弹出堆栈。接下来,将遇到对 networkRequest() 的调用,因此将它推到堆栈的顶部。

下一个 setTimeout() 函数被调用,因此它被推到堆栈的顶部。setTimeout()有两个参数:

1) 回调和

2) 以毫秒(ms)为单位的时间。

setTimeout() 方法在web api环境中启动一个2s的计时器。此时,setTimeout()已经完成,并从堆栈中弹出。cosole.log(“the end”) 被推送到堆栈中,在完成后执行并从堆栈中删除。

同时,计时器已经过期,现在回调被推送到消息队列。但是回调不会立即执行,这就是事件轮询开始的地方。

事件轮询

事件轮询的工作是监听调用堆栈,并确定调用堆栈是否为空。如果调用堆栈是空的,它将检查消息队列,看看是否有任何挂起的回调等待执行。

在这种情况下,消息队列包含一个回调,此时调用堆栈为空。因此,事件轮询将回调推到堆栈的顶部。

然后是 console.log(“Async Code”) 被推送到堆栈顶部,执行并从堆栈中弹出。此时,回调已经完成,因此从堆栈中删除它,程序最终完成。

消息队列还包含来自DOM事件(如单击事件和键盘事件)的回调。例如:

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

登录后复制

对于DOM事件,事件侦听器位于web api环境中,等待某个事件(在本例中单击event)发生,当该事件发生时,回调函数被放置在等待执行的消息队列中。

同样,事件轮询检查调用堆栈是否为空,并在调用堆栈为空并执行回调时将事件回调推送到堆栈。

延迟函数执行

我们还可以使用setTimeout来延迟函数的执行,直到堆栈清空为止。例如

const bar = () => {  console.log('bar');}const baz = () => {  console.log('baz');}const foo = () => {  console.log('foo');  setTimeout(bar, 0);  baz();}foo();

登录后复制

打印结果:

foobazbar

登录后复制

当这段代码运行时,第一个函数foo()被调用,在foo内部我们调用console.log(‘foo’),然后setTimeout()被调用,bar()作为回调函数和时0秒计时器。

现在,如果我们没有使用 setTimeout, bar() 函数将立即执行,但是使用 setTimeout 和0秒计时器,将bar的执行延迟到堆栈为空的时候。

0秒后,bar()回调被放入等待执行的消息队列中。但是它只会在堆栈完全空的时候执行,也就是在baz和foo函数完成之后。

ES6 任务队列

我们已经了解了异步回调和DOM事件是如何执行的,它们使用消息队列存储等待执行所有回调。

ES6引入了任务队列的概念,任务队列是 JavaScript 中的 promise 所使用的。消息队列和任务队列的区别在于,任务队列的优先级高于消息队列,这意味着任务队列中的promise 作业将在消息队列中的回调之前执行,例如:

const bar = () => {  console.log('bar');};const baz = () => {  console.log('baz');};const foo = () => {  console.log('foo');  setTimeout(bar, 0);  new Promise((resolve, reject) => {    resolve('Promise resolved');  }).then(res => console.log(res))    .catch(err => console.log(err));  baz();};foo();

登录后复制

打印结果:

foobazPromised resolvedbar

登录后复制

我们可以看到 promise 在 setTimeout 之前执行,因为 promise 响应存储在任务队列中,任务队列的优先级高于消息队列。

小结

因此,我们了解了异步 JavaScript 是如何工作的,以及调用堆栈、事件循环、消息队列和任务队列等概念,这些概念共同构成了 JavaScript 运行时环境。虽然成为一名出色的JavaScript开发人员并不需要学习所有这些概念,但是了解这些概念是有帮助的:)

以上就是深入剖析JavaScript异步之事件轮询的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2636008.html

(0)
上一篇 2025年3月7日 04:57:17
下一篇 2025年3月7日 04:57:22

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • node.js如何为PDF添加水印(代码示例)

    本篇文章给大家带来的内容是关于node.js如何为pdf添加水印(代码示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助 项目中有用到node做中间层,期间有个为PDF文件添加水印的需求,网上搜了不少帖子,发现有以下几点存…

    2025年3月7日
    200
  • JavaScript中异步编程的方法有哪些?JavaScript异步编程的方法介绍

    本篇文章给大家带来的内容是关于javascript中异步编程的方法有哪些?javascript异步编程的方法介绍,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 我们知道Javascript语言的执行环境是”单线…

    2025年3月7日
    200
  • webpack原理的深入介绍(附示例)

    本篇文章给大家带来的内容是关于webpack原理的深入介绍(附示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 本文抄自《深入浅出webpack》,建议想学习原理的手打一遍,操作一遍,给别人讲一遍,然后就会了 在阅读前…

    2025年3月7日
    200
  • JavaScript运行原理的详细介绍

    本篇文章给大家带来的内容是关于javascript运行原理的详细介绍,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 JavaScript运行原理 知其然,也要知其所以然,这里主要谈一谈对JavaScript运行原理的理解。…

    编程技术 2025年3月7日
    200
  • JavaScript中Symbol 类型有什么用?

    本篇文章给大家带来的内容是关于JavaScript中Symbol 类型有什么用?有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 Symbols 是 ES6 引入了一个新的数据类型 ,它为 JS 带来了一些好处,尤其是对象属性…

    编程技术 2025年3月7日
    200
  • HTML转成PDF的4个方法介绍(附代码)

    本篇文章给大家带来的内容是关于把html转成pdf的4个方法介绍(附代码),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 在本文中,我将展示如何使用 Node.js、Puppeteer、headless Chrome 和 …

    编程技术 2025年3月7日
    200
  • Node.js中package.json中库的版本号的介绍

    本篇文章给大家带来的内容是关于Node.js中package.json中库的版本号的介绍,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 ~和^的区别 最近总是碰到一些问题, 在本地好好的, 在线上就出现了问题, 本地也一直…

    编程技术 2025年3月7日
    200
  • JavaScript在nodejs环境下执行机制和事件循环

    本篇文章给大家带来的内容是关于JavaScript高阶函数的用法介绍,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 1、说明 nodejs是单线程执行的,同时它又是基于事件驱动的非阻塞IO编程模型。这就使得我们不用等待异步…

    2025年3月7日
    200
  • 基于TensorFlow.js的JavaScript机器学习

    本篇文章讲述的是基于tensorflow.js的javascript机器学习,具有一定参考价值,感兴趣的朋友可以了解一下。 虽然python或r编程语言有一个相对容易的学习曲线,但是Web开发人员更喜欢在他们舒适的javascript区域内…

    2025年3月7日 编程技术
    200
  • java开发要学什么前端技术?

    现如今,网络技术日渐发达,程序员们也要学习更多技术才不会在瞬息变幻的潮流中被迫更替,今天我将和大家谈谈,java开发需要学习的一些前端技术。 对于这个Java开发人员,不仅要知道后端的知识,前端知识是基本上也是必学的。 我感觉原因有以下几点…

    2025年3月7日
    200

发表回复

登录后才能评论