作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
穆罕默德·阿马尔·伊利亚斯
验证专家 在工程
11 的经验

Muhammad是一名全栈开发人员,拥有超过十年的经验,为各种公司提供web和移动应用程序, 包括观测仪的, Microblink, 和Resulta. He has JavaScript expertise ranging across Node.js,下一个.js, 反应, 反应 Native, and headless WordPress solutions.

以前的角色

高级WordPress开发人员
分享

作为开发者, I naturally want my software to be reliable and responsive. In the early days of my career, feedback on my applications was mixed. 一些应用获得了很高的评价, 但对其他应用程序的评论却不一致,因为它们会在中途间歇性地停止响应——我们都知道终端用户对糟糕的程序响应是多么没有耐心.

The underlying issue was that the apps were coded using purely synchronous JavaScript. Since JavaScript offers (seemingly) asynchronous functions, 很容易忽略一个事实,即JavaScript运行时本身在默认情况下是同步的, and this is a potential pitfall for developers. My curiosity drove me to investigate this programmatic puzzle.

The Problem: JavaScript Synchronous Blocking

I started my exploration by observing the way that regular, 同步调用工作, focusing my efforts on call stacks—last in, first out (LIFO) programming 结构.

All call stacks function alike, regardless of the language: We (add) function calls to the stack and then 流行 如有需要,可将其移除.

让我们考虑一个简短的例子:

函数乘法(a, b) {
    返回a * b;
}

函数平方(n) {
    返回乘(n, n);
}

函数printSquare(n) {
    const 广场dNum = 广场(n);
    控制台.日志(广场dNum);
}

printSquare (4);

In our example, the outermost function, printSquare,叫。 广场 函数,该函数反过来调用 . Functions are added to our call stack in the order they are encountered. As each method is completed, it is removed from the end of the call stack (i.e., 会首先被移除).

一列标记为调用堆栈,其中包含标记为(从下到上)的单元格:, 广场(4), 和繁殖(4, 4).
JavaScript调用栈示例

Since the call stack is synchronous, when one or more of these functions takes significant time to complete, 剩余的任务被阻塞. 我们的程序变得无响应——至少是暂时的——并且只有在被阻塞的函数完成时才恢复.

Common function calls resulting in these program delays include:

  • A loop with a high iteration count (e.g.(从1到1万亿).
  • A network request to an external web server.
  • An event that waits for a timer to complete.
  • 图像处理.

在web浏览器中为最终用户提供, 同步调用阻塞导致无法与页面元素交互. 对于开发者来说, 这些卡住的调用使开发控制台无法访问,并剥夺了检查详细调试信息的能力.

The Solution: 异步 JavaScript Functionality

异步 coding is a programming technique in which, 在调用函数之后, 代码的其余部分无需等待初始函数返回就可以运行. When an asynchronous task completes, the JavaScript runtime passes the result to a function of our choosing. This method eliminates obstacles for our end users and developers.

JavaScript实现 异步功能 via a few key architectural components:

An animation showing the interaction and flow between the JavaScript call stack, 浏览器API, and task queue that support asynchronous functions.
JavaScript的异步流

Anything that needs to run asynchronously (e.g.(计时器或外部API调用)被发送到运行时引擎的浏览器API (web API)。. The 浏览器API spawns a single execution thread per operation routed its way.

发送到浏览器API的每个异步JavaScript函数调用都有一个相应的承诺,允许在函数完成(成功或不成功)时触发处理程序代码。. 当函数完成时——不管它是否返回值——它的返回值将履行其相关的承诺, and the function moves from the 浏览器API into JavaScript’s task queue.

The key player in JavaScript’s asynchronous processing is its 事件循环. The 事件循环 continuously checks if the call stack and task queue are empty, 当那些完成的异步调用应该被推回主调用堆栈时的坐标.

现在让我们检查一下JavaScript setTimeout method to see JavaScript’s asynchronous method handling in action:

函数a() {
    b();
}

函数b() {
    setTimeout(() => {
        控制台.日志(“5秒后”);
    }, 5000);
}

函数c() {
    控制台.日志(“Hello World”);
}

a();
c();

一个动画,显示了从JavaScript调用堆栈到浏览器API和前面代码示例的任务队列的详细流程.
浏览器API如何处理 setTimeout的函数

让我们看一下代码:

  1. a 转到调用堆栈.
  2. b’s setTimeout invocation is moved to the 浏览器API call stack.
  3. c 转到调用堆栈.
  4. c’s 控制台.日志 将调用推入调用堆栈.
  5. setTimeout method completes, it is moved from the 浏览器API to the task queue.
  6. Any functions within the call stack process to completion.
  7. 当 call stack empties, the 事件循环 moves the setTimeout’s function from the task queue back into the call stack.

软件工程师 可以通过应用这些JavaScript异步方法来扩展他们的开发能力吗. 现在我们已经看到了JavaScript运行时中的异步方法是如何处理的, I’ll demonstrate their applicability with a short example.

Real-world Applications: A Chatbot Example

I recently developed a browser-based 聊天机器人. 同步行为是不可取的,因为它会导致对话显得脱节和迟缓. 我的解决方案通过异步地与 ChatGPT external API to both send and receive messages.

To facilitate communication with the ChatGPT API,我创建了一个简单的Node.Js服务器使用 OpenAI. 然后我利用 异步JavaScript 获取 API 它使用程序化承诺来提供一种访问和处理响应的方式:

  获取 (http://localhost: 5000 /, {
    方法:“文章”,
    标题:{
      “内容类型”:“application / json”
    },
    身体:JSON.stringify ({
      query: 'What is the weather like in Seattle?'
    })
  })
  .then(response => response.json ())
  .then(data => {
    控制台.日志(数据);
  });

Our simple server asynchronously calls the ChatGPT service 而 providing bidirectional message transmission.

另一个异步方法I 常用的是 setInterval (). 该函数提供了一个内置计时器,该计时器随后以任何指定的间隔重复调用函数. 使用 setInterval, I added a typing effect to the user interface, 让用户知道另一方(聊天机器人)正在创建响应:

// Creating loader function for bot
函数加载器(元素){
    元素.textContent = ";

    // 300 ms allows for real-time responsiveness indicating other-party typing
    loadInterval = setInterval(() => {
        元素.textContent += '.';

        如果(元素.textContent === '....') {
            元素.textContent = ";
        }
    }, 300);
}

//创建输入功能
function typeText(元素, text) {
    设index = 0;
    // 20 ms allows for real-time responsiveness to mimic chat typing
    let interval = setInterval(() => {
        if (index < text.长度){
            元素.innerHTML += text.charAt(指数);
            指数+ +;
        } else {
            clearInterval(间隔);
        }
    }, 20);
}

这两个异步模块将原本脱节的对话变成了参与者感兴趣的对话. 但是异步JavaScript允许的响应性在其他上下文中可能是一个不太明显的关键因素.

More 异步 JavaScript Examples

Once I was tasked with creating a custom WordPress plugin that allowed users to upload large files asynchronously. 我用了 AJAX库 允许用户在后台上传他们的文件,而不必等待页面重新加载. 这使得用户体验更加流畅,应用程序获得了巨大的成功.

在另一个用例中 电子商务网站 是否因为加载大量图片而导致加载时间过慢. To speed up the process, I implemented an async JavaScript function (LazyLoading) to load each image asynchronously. 这使得网站加载速度更快,因为图片不是同时加载的.

我还参与了一个涉及汇款应用程序的项目,该应用程序集成了各种加密和支付api. 我需要从外部API提取数据,但是API需要一些时间来响应. 以确保应用程序在等待API时不会陷入停顿, 我实现了一个async函数,它能够在应用程序等待API响应时保持运行, resulting in an enhanced user experience.

JavaScript实现中的异步方法为最终用户的服务提供了强大的功能, 减少UI减速或冻结. 这就是为什么异步JavaScript对Uber等应用的用户留存至关重要 付款流程 在后台), Twitter (loading the latest tweets in real time), and Dropbox (keeping users’ files synced and up to date across devices).

作为开发者, 您可能会担心异步JavaScript方法不会像预期的那样出现在调用堆栈中,但请放心, 他们所做的. 您可以自信地将异步功能包含在提供卓越用户体验的选项中.

The Toptal 工程 博客 extends its gratitude to 穆罕默德·阿西姆·比拉尔 for reviewing the technical content and code samples presented in this article.

了解基本知识

  • 什么是异步JavaScript?

    异步编程允许在不阻塞后续处理的情况下执行代码语句. JavaScript运行时模拟异步功能,同时在后台使用一组同步队列.

  • Is JavaScript asynchronous by default?

    No. 默认情况下, JavaScript是同步的, 单线程编程语言,其中指令一个接一个地运行,而不是并行运行. 异步 techniques don’t change this fundamental limitation, but 他们所做的 help reduce programmatic blocking.

  • What are the benefits of asynchronous programming?

    异步编程允许应用程序通过避免减速或前端冻结来保持响应.

聘请Toptal这方面的专家.
现在雇佣
穆罕默德·阿马尔·伊利亚斯

穆罕默德·阿马尔·伊利亚斯

验证专家 在工程
11 的经验

拉合尔,旁遮普,巴基斯坦

自2021年7月14日起成为会员

作者简介

Muhammad是一名全栈开发人员,拥有超过十年的经验,为各种公司提供web和移动应用程序, 包括观测仪的, Microblink, 和Resulta. He has JavaScript expertise ranging across Node.js,下一个.js, 反应, 反应 Native, and headless WordPress solutions.

作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.

以前的角色

高级WordPress开发人员

World-class articles, delivered weekly.

Subscription implies consent to our 隐私政策

World-class articles, delivered weekly.

Subscription implies consent to our 隐私政策

Toptal开发者

加入总冠军® 社区.