【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

相关文章
|
17天前
|
JavaScript 前端开发
【JavaScript】——JS基础入门常见操作(大量举例)
JS引入方式,JS基础语法,JS增删查改,JS函数,JS对象
|
2月前
|
JSON 前端开发 JavaScript
在 JavaScript 中,如何使用 Promise 处理异步操作?
通过以上方式,可以使用Promise来有效地处理各种异步操作,使异步代码更加清晰、易读和易于维护,避免了回调地狱的问题,提高了代码的质量和可维护性。
|
2月前
|
JavaScript 前端开发 Java
springboot解决js前端跨域问题,javascript跨域问题解决
本文介绍了如何在Spring Boot项目中编写Filter过滤器以处理跨域问题,并通过一个示例展示了使用JavaScript进行跨域请求的方法。首先,在Spring Boot应用中添加一个实现了`Filter`接口的类,设置响应头允许所有来源的跨域请求。接着,通过一个简单的HTML页面和jQuery发送AJAX请求到指定URL,验证跨域请求是否成功。文中还提供了请求成功的响应数据样例及请求效果截图。
springboot解决js前端跨域问题,javascript跨域问题解决
|
2月前
|
JavaScript 前端开发
Moment.js与其他处理时间戳格式差异的JavaScript库相比有什么优势?
Moment.js与其他处理时间戳格式差异的JavaScript库相比有什么优势?
|
2月前
|
JSON JavaScript 前端开发
使用JavaScript和Node.js构建简单的RESTful API
使用JavaScript和Node.js构建简单的RESTful API
|
3月前
|
前端开发 JavaScript 开发者
JS 异步解决方案的发展历程以及优缺点
本文介绍了JS异步解决方案的发展历程,从回调函数到Promise,再到Async/Await,每种方案的优缺点及应用场景,帮助开发者更好地理解和选择合适的异步处理方式。
|
3月前
|
人工智能 JavaScript 前端开发
使用Node.js模拟执行JavaScript
使用Node.js模拟执行JavaScript
39 2
|
3月前
|
Web App开发 JavaScript 前端开发
Node.js:JavaScript世界的全能工具
Node.js:JavaScript世界的全能工具
|
3月前
|
JSON JavaScript 前端开发
使用JavaScript和Node.js构建简单的RESTful API服务器
【10月更文挑战第12天】使用JavaScript和Node.js构建简单的RESTful API服务器
34 0
|
8月前
|
前端开发 JavaScript
如何处理 JavaScript 中的异步操作和 Promise?
如何处理 JavaScript 中的异步操作和 Promise?
77 1