authors are vetted experts in their fields and write on topics in which they have demonstrated experience. All of our content is peer reviewed and validated by Toptal experts in the same field.
穆罕默德·阿马尔·伊利亚斯
验证专家 在工程
11 的经验

Muhammad is a full-stack developer with more than a decade of experience delivering web and mobile applications for various companies, 包括观测仪的, Microblink, 和Resulta. 他拥有跨Node的JavaScript专业知识.js,下一个.js、反应、反应 Native和WordPress解决方案.

以前的角色

高级WordPress开发人员

以前在

观测仪的
分享

作为一名开发人员,我自然希望我的软件可靠且响应迅速. 在我职业生涯的早期,对我申请的反馈褒贬不一. 一些应用获得了很高的评价, but reviews were inconsistent on other apps because they would intermittently stop responding midsession—and we all know how little patience end users have for poor program responsiveness.

潜在的问题是,应用程序是使用纯同步编码的 JavaScript. 因为JavaScript提供了(看似)异步函数, it’s easy to miss the fact that JavaScript’s runtime itself is synchronous by default, 这对开发人员来说是一个潜在的陷阱. 我的好奇心驱使我去研究这个程序性的难题.

问题:JavaScript同步阻塞

我的探索是从观察规律的方式开始的, 同步调用工作, 把我的精力集中在调用堆栈上后进先出(LIFO)编程 结构.

不管使用什么语言,所有调用堆栈的功能都是一样的 (add)函数调用栈,然后 流行 如有需要,可将其移除.

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

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

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

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

printSquare (4);

在我们的例子中,最外面的函数, printSquare,叫。 广场 函数,该函数反过来调用 . 函数按照遇到的顺序被添加到调用堆栈中. 当每个方法完成时,它将从调用堆栈的末尾(i.e., 会首先被移除).

A column labeled call stack containing cells that are labeled (from bottom to top): printSquare(4), 广场(4), 和繁殖(4, 4).
JavaScript调用栈示例

因为调用堆栈是同步的, 当其中一个或多个功能需要花费大量时间才能完成时, 剩余的任务被阻塞. Our program becomes unresponsive—at least temporarily—and resumes only when the blocked function is completed.

导致这些程序延迟的常见函数调用包括:

  • A 具有高迭代计数的循环(例如.g.(从1到1万亿).
  • 向外部web服务器发出的网络请求.
  • 等待计时器完成的事件.
  • 图像处理.

在web浏览器中为最终用户提供, synchronous call blockages result in an inability to interact with page 元素s. 对于开发者来说, these stuck calls make the development 控制台 inaccessible and take away the ability to examine detailed debugging information.

解决方案:异步JavaScript功能

异步编码是一种编程技术, 在调用函数之后, the remainder of our code can run without having to wait for the initial function to return. 当异步任务完成时, JavaScript运行时将结果传递给我们选择的函数. 这种方法为我们的最终用户和开发人员消除了障碍.

JavaScript实现 异步功能 通过几个关键的架构组件:

显示JavaScript调用栈之间的交互和流程的动画, 浏览器API, 以及支持异步功能的任务队列.
JavaScript的异步流

任何需要异步运行的内容(例如.g., a timer or external API call) is sent to the runtime engine’s 浏览器API (web API). 浏览器API为每个操作生成一个执行线程.

Each 异步JavaScript function call sent to the 浏览器API has a corresponding promise that allows handler code to be triggered when the function completes (either successfully or unsuccessfully). 当 function completes—regardless of whether it returns a value—its return fulfills its associated promise, 函数从浏览器API移到JavaScript的任务队列中.

JavaScript异步处理的关键是它的 事件循环. 事件循环不断检查调用堆栈和任务队列是否为空, and coordinates when those completed asynchronous calls should be 推ed back onto the main call stack.

现在让我们检查一下JavaScript setTimeout 方法查看JavaScript的异步方法处理:

函数a() {
    b();
}

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

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

a();
c();

An animation showing a detailed flow from JavaScript’s call stack into the 浏览器API and task queue for the preceding code example.
浏览器API如何处理 setTimeout的函数

让我们看一下代码:

  1. a 转到调用堆栈.
  2. b’s setTimeout 调用被移动到浏览器API调用堆栈.
  3. c 转到调用堆栈.
  4. c’s 控制台.日志 将调用推入调用堆栈.
  5. setTimeout 方法完成后,它将从浏览器API移到任务队列中.
  6. 调用堆栈内的任何函数进程来完成.
  7. 当调用堆栈清空时,事件循环移动 setTimeout的函数从任务队列返回到调用堆栈.

软件工程师 can expand their development capabilities through the application of these JavaScript asynchronous methods. Now that we have seen how asynchronous methods within the JavaScript runtime are handled, 我将通过一个简短的示例来演示它们的适用性.

真实世界的应用:一个聊天机器人的例子

我最近开发了一个基于浏览器的 聊天机器人. Synchronous behavior would have been undesirable as it would cause the conversation to appear disjointed and sluggish. My solution achieves well-paced conversation by asynchronously communicating with the ChatGPT 发送和接收消息的外部API.

方便与之沟通 ChatGPT API,我创建了一个简单的Node.Js服务器使用 OpenAI. 然后我利用 异步JavaScript 获取 API that uses programmatic promises to provide a way to access and process responses:

  获取 (http://localhost: 5000 /, {
    方法:“文章”,
    标题:{
      “内容类型”:“application / json”
    },
    身体:JSON.stringify ({
      问:西雅图的天气怎么样?'
    })
  })
  .then(response => response.json ())
  .then(data => {
    控制台.日志(数据);
  });

我们的简单服务器异步调用 ChatGPT 服务的同时提供双向消息传输.

另一个异步方法I 常用的是 setInterval (). This function provides a built-in timer that subsequently calls a function repeatedly at any specified interval. 使用 setInterval, 我在用户界面中添加了打字效果, letting the user know that the other party (the 聊天机器人) is creating a response:

//为bot创建loader函数
函数加载器(元素){
    元素.textContent = ";

    // 300毫秒允许实时响应指示对方输入
    loadInterval = setInterval(() => {
        元素.textContent += '.';

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

//创建输入功能
函数typeText(元素,文本){
    设index = 0;
    // 20ms允许模拟聊天输入的实时响应
    let interval = setInterval(() => {
        if (index < text.长度){
            元素.innerHTML += text.charAt(指数);
            指数+ +;
        } else {
            clearInterval(间隔);
        }
    }, 20);
}

These two asynchronous blocks turn an otherwise disjointed conversation into one in which participants feel engaged. But the responsiveness 异步JavaScript allows may be a less obvious key ingredient in other contexts.

更多异步JavaScript示例

有一次我的任务是创建一个自定义 WordPress 允许用户异步上传大文件的插件. 我用了 AJAX库 to allow the user to upload their files in the background without having to wait for the page to reload. This allowed for a much smoother user experience and the application was a huge success.

在另一个用例中 电子商务网站 was having trouble with slow loading times due to the large number of images it had to load. 为了加快这个过程,我实现了一个async JavaScript函数(LazyLoading)以异步加载每个图像. This allowed the website to load faster, as the images weren’t all loaded at the same time.

I also worked on a project involving a money transfer application integrating various crypto and payment APIs. I needed to pull data from an external API, but the API took some time to respond. 以确保应用程序在等待API时不会陷入停顿, I implemented an async function that was able to keep the application running 而 it waited for the API response, 从而增强用户体验.

异步 methods in a JavaScript implementation allow for powerful functionality in the service of end users, 减少UI减速或冻结. That’s why 异步JavaScript is crucial to user retention in apps like Uber (running its booking and 付款流程 在后台), Twitter(实时加载最新推文), Dropbox(让用户的文件在不同设备间保持同步和最新).

作为开发者, you may worry that 异步JavaScript methods won’t appear on the call stack as expected—but rest assured, 他们所做的. You may confidently include 异步功能 among your options in delivering superior user experiences.

Toptal 工程博客向 穆罕默德·阿西姆·比拉尔 查看本文中提供的技术内容和代码示例.

了解基本知识

  • 什么是异步JavaScript?

    异步 programming allows code statements to execute without blocking subsequent processing. JavaScript runtimes simulate 异步功能 而 using a synchronous set of queues behind the scenes.

  • JavaScript默认是异步的吗?

    No. 默认情况下, JavaScript是同步的, single-threaded programming language in which instructions run one after the other—not in parallel. 异步技术并没有改变这一基本限制, 但它们确实有助于减少程序阻塞.

  • 异步编程的好处是什么?

    异步 programming allows an application to stay responsive by avoiding slowdowns or front-end freezes.

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

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

验证专家 在工程
11 的经验

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

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

作者简介

Muhammad is a full-stack developer with more than a decade of experience delivering web and mobile applications for various companies, 包括观测仪的, Microblink, 和Resulta. 他拥有跨Node的JavaScript专业知识.js,下一个.js、反应、反应 Native和WordPress解决方案.

authors are vetted experts in their fields and write on topics in which they have demonstrated experience. All of our content is peer reviewed and validated by Toptal experts in the same field.

以前的角色

高级WordPress开发人员

以前在

观测仪的

世界级的文章,每周发一次.

输入您的电子邮件,即表示您同意我们的 隐私政策.

世界级的文章,每周发一次.

输入您的电子邮件,即表示您同意我们的 隐私政策.

Toptal开发者

加入总冠军® 社区.