【JavaScript】这一次,彻底搞懂 JS 异步及其演进历程 ~(一)

简介: 【JavaScript】这一次,彻底搞懂 JS 异步及其演进历程 ~(一)

JavaScript 是一种单线程语言,这意味着在任何给定的实例中,JavaScript 的引擎(在托管环境中运行,如标准 Web 浏览器)一次只能执行一条语句或一行代码。在浏览器中加载 JavaScript 文件时,JavaScript 引擎会从上到下处理文件中的每一行。

虽然单线程语言简化了代码编写,因为你不必担心并发问题,但这也意味着你无法在不阻塞主线程的情况下执行网络访问等耗时操作。

  • 想象一下从 API 请求一些数据。根据情况,服务器可能需要一些时间来处理请求,同时阻塞主线程使网页无响应。而使用异步 JavaScript(例如回调、promises 和 async/await),你可以在不阻塞主线程的情况下执行耗时的网络请求。
  • 当该线程在执行某些耗时操作时(比如网络请求、文件读取等),如果这个操作是同步的,那么主线程会被完全阻塞,无法响应任何其他的用户交互事件,导致整个页面失去响应。这就是所谓的“UI 阻塞”。

为了避免这种情况的发生,JavaScript 引入了异步编程的概念。异步编程的本质是在主线程发起一个耗时操作时,不必等待其完成,而是立即返回给主线程并继续执行其他代码。当操作完成后,又将相应的结果通知到主线程。

JavaScript 的引擎在浏览器上运行,但它并不是孤立运行的。Web 浏览器结合了 JavaScript 引擎和其他附加功能(Web API),在 JavaScript 中对其进行了标记。这些 Web API(setTimeout、setInterval 等)允许 JavaScript 异步运行,允许正常的同步函数在异步任务完成时继续运行。

具体来说,异步编程使用回调函数PromiseGeneratorasync/await 等方式来实现,在执行异步操作时,主线程将异步任务委托给浏览器的底层引擎,通过 Event Loop 机制实现对异步操作的检测和处理,使得主线程能够及时响应用户的交互事件,提高了 JavaScript 的性能和用户体验。

因此,JavaScript 之所以需要异步编程,是为了提供更好的用户体验及改善性能。接下来我们就去回顾一下异步 JavaScript 的演进历程。

一、什么是异步?

1、调用堆栈(Call Stack)

调用堆栈是JavaScript中的一个结构,用来暂时保存函数调用的列表。一旦你的JavaScript应用程序开始执行,就会在调用栈的底部创建并添加一个全局执行环境。因为JavaScript是单线程的,这个页面上的所有东西都是由一个单线程执行的。这个执行线程开始一行一行地遍历你的代码。

因为JavaScript一次只能执行一件事,所以它使用调用栈来跟踪已经被调用的函数、已经被调用但还没有完成的函数等的执行环境。当一个函数被调用时,一个新的执行上下文被创建,对该函数的引用被添加到调用栈的顶部,在那里开始执行。当函数的执行完成后,就会从堆栈中移除,JavaScript的引擎就会开始执行顶部的下一个函数。

就像这样:

微信图片_20230615160819.png它被称为调用堆栈,因为它使用了数据结构中堆栈的概念,遵循后进先出(LIFO)原则。它总是会先处理堆栈顶部的调用。例如,如果你在桌子上有一堆盘子,想添加更多的盘子,你就把它们添加到顶部,如果你要从这堆盘子中拿一个或多个盘子,你就从顶部拿,而不是从底部。最后添加到这堆盘子里的盘子将是第一个被拿走的。

这与调用栈的情况相同。栈顶的函数总是首先被执行。在执行函数并将其添加到调用栈时,如果JavaScript遇到一个使用Web API方法的异步函数,它不会将其添加到调用栈中,而是将其发送到Web API容器中。

2、事件队列(Event Queue)

如果JavaScript遇到一个使用Web API方法的异步函数,它会将其发送到Web API容器中。随后,每当所需的事件发生时,该方法就会从 Web API容器 转移到事件队列中。例如,setTimeout中的回调在指定时间过后被添加到队列中。

与调用堆栈不同,事件队列遵循 FIFO(先进先出)原则,这意味着调用是按照它们被添加到队列的顺序处理的。

3、事件循环(Event Loop)

事件循环是一个无限期运行的循环,作为调用栈和事件队列之间的连接。事件循环反复检查调用栈,并将子任务从事件队列转移到调用栈。在事件循环开始从队列中转移子任务之前,调用堆栈必须是空的,这表明所有程序的常规(同步)功能已经被执行。

如果调用堆栈是空的,添加到事件队列中的第一个子任务(最老的那个)将从队列中移除,其相关的函数将被添加到调用堆栈中,并以该子任务为参数执行。

就像这样:

image.png

相关文章
|
6月前
|
资源调度 JavaScript 前端开发
Day.js极简轻易快速2kB的JavaScript库-替代Moment.js
dayjs是一个极简快速2kB的JavaScript库,可以为浏览器处理解析、验证、操作和显示日期和时间,它的设计目标是提供一个简单、快速且功能强大的日期处理工具,同时保持极小的体积(仅 2KB 左右)。
375 24
|
9月前
|
Web App开发 JavaScript 前端开发
如何在JavaScript中确定异步操作之间的依赖关系?
如何在JavaScript中确定异步操作之间的依赖关系?
218 58
|
9月前
|
前端开发 JavaScript
有没有方法可以保证在JavaScript中多个异步操作的执行顺序?
有没有方法可以保证在JavaScript中多个异步操作的执行顺序?
400 58
|
9月前
|
JavaScript 前端开发
JavaWeb JavaScript ③ JS的流程控制和函数
通过本文的详细介绍,您可以深入理解JavaScript的流程控制和函数的使用,进而编写出高效、可维护的代码。
210 32
|
8月前
|
JavaScript 前端开发 算法
JavaScript 中通过Array.sort() 实现多字段排序、排序稳定性、随机排序洗牌算法、优化排序性能,JS中排序算法的使用详解(附实际应用代码)
Array.sort() 是一个功能强大的方法,通过自定义的比较函数,可以处理各种复杂的排序逻辑。无论是简单的数字排序,还是多字段、嵌套对象、分组排序等高级应用,Array.sort() 都能胜任。同时,通过性能优化技巧(如映射排序)和结合其他数组方法(如 reduce),Array.sort() 可以用来实现高效的数据处理逻辑。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
8月前
|
JavaScript 前端开发 API
JavaScript中通过array.map()实现数据转换、创建派生数组、异步数据流处理、复杂API请求、DOM操作、搜索和过滤等,array.map()的使用详解(附实际应用代码)
array.map()可以用来数据转换、创建派生数组、应用函数、链式调用、异步数据流处理、复杂API请求梳理、提供DOM操作、用来搜索和过滤等,比for好用太多了,主要是写法简单,并且非常直观,并且能提升代码的可读性,也就提升了Long Term代码的可维护性。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
8月前
|
消息中间件 JavaScript 前端开发
最细最有条理解析:事件循环(消息循环)是什么?为什么JS需要异步
度一教育的袁进老师谈到他的理解:单线程是异步产生的原因,事件循环是异步的实现方式。 本质是因为渲染进程因为计算机图形学的限制,只能是单线程。所以需要“异步”这个技术思想来解决页面阻塞的问题,而“事件循环”是实现“异步”这个技术思想的最主要的技术手段。 但事件循环并不是全部的技术手段,比如Promise,虽然受事件循环管理,但是如果没有事件循环,单一Promise依然能实现异步不是吗? 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您
|
8月前
|
数据采集 JavaScript 前端开发
JavaScript中通过array.filter()实现数组的数据筛选、数据清洗和链式调用,JS中数组过滤器的使用详解(附实际应用代码)
用array.filter()来实现数据筛选、数据清洗和链式调用,相对于for循环更加清晰,语义化强,能显著提升代码的可读性和可维护性。博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
11月前
|
JavaScript 前端开发
【JavaScript】——JS基础入门常见操作(大量举例)
JS引入方式,JS基础语法,JS增删查改,JS函数,JS对象

热门文章

最新文章

下一篇
oss云网关配置