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

Muhammad是一名全栈开发人员,拥有10年为Resulta和其他大型营销公司提供web和移动应用程序的经验. 他拥有跨Node的JavaScript专业知识.js,下一个.js、React、React Native和WordPress解决方案.

阅读更多
分享

作为一名开发人员,我自然希望我的软件可靠且响应迅速. 在我职业生涯的早期,对我申请的反馈褒贬不一. 一些应用获得了很高的评价, 但对其他应用程序的评论却不一致,因为它们会在中途间歇性地停止响应——我们都知道终端用户对糟糕的程序响应是多么没有耐心.

潜在的问题是,应用程序是使用纯同步编码的 JavaScript. 因为JavaScript提供了(看似)异步函数, 很容易忽略一个事实,即JavaScript运行时本身在默认情况下是同步的, 这对开发人员来说是一个潜在的陷阱. 我的好奇心驱使我去研究这个程序性的难题.

问题:JavaScript同步阻塞

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

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

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

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

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

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

printSquare (4);

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

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

因为调用堆栈是同步的, 当其中一个或多个功能需要花费大量时间才能完成时, 剩余的任务被阻塞. 我们的程序变得无响应——至少是暂时的——并且只有在被阻塞的函数完成时才恢复.

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

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

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

解决方案:异步JavaScript功能

异步编码是一种编程技术, 在调用函数之后, 代码的其余部分无需等待初始函数返回就可以运行. 当异步任务完成时, JavaScript运行时将结果传递给我们选择的函数. 这种方法为我们的最终用户和开发人员消除了障碍.

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

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

任何需要异步运行的内容(例如.g.(计时器或外部API调用)被发送到运行时引擎的浏览器API (web API)。. 浏览器API为每个操作生成一个执行线程.

发送到浏览器API的每个异步JavaScript函数调用都有一个相应的承诺,允许在函数完成(成功或不成功)时触发处理程序代码。. 当函数完成时——不管它是否返回值——它的返回值将履行其相关的承诺, 函数从浏览器API移到JavaScript的任务队列中.

JavaScript异步处理的关键是它的 事件循环. 事件循环不断检查调用堆栈和任务队列是否为空, 当那些完成的异步调用应该被推回主调用堆栈时的坐标.

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

函数a() {
    b();
}

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

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

a();
c();

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

让我们看一下代码:

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

软件工程师 可以通过应用这些JavaScript异步方法来扩展他们的开发能力吗. 现在我们已经看到了JavaScript运行时中的异步方法是如何处理的, 我将通过一个简短的示例来演示它们的适用性.

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

我最近开发了一个基于浏览器的 聊天机器人. 同步行为是不可取的,因为它会导致对话显得脱节和迟缓. 我的解决方案通过异步地与 ChatGPT 发送和接收消息的外部API.

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

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

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

另一个异步方法I 常用的是 setInterval (). 该函数提供了一个内置计时器,该计时器随后以任何指定的间隔重复调用函数. 使用 setInterval, 我在用户界面中添加了打字效果, 让用户知道另一方(聊天机器人)正在创建响应:

//为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);
}

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

更多异步JavaScript示例

有一次我的任务是创建一个自定义 WordPress 允许用户异步上传大文件的插件. 我用了 AJAX库 允许用户在后台上传他们的文件,而不必等待页面重新加载. 这使得用户体验更加流畅,应用程序获得了巨大的成功.

在另一个用例中 电子商务网站 是否因为加载大量图片而导致加载时间过慢. 为了加快这个过程,我实现了一个async JavaScript函数(LazyLoading)以异步加载每个图像. 这使得网站加载速度更快,因为图片不是同时加载的.

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

JavaScript实现中的异步方法为最终用户的服务提供了强大的功能, 减少UI减速或冻结. 这就是为什么异步JavaScript对Uber等应用的用户留存至关重要 付款流程 在后台), Twitter(实时加载最新推文), Dropbox(让用户的文件在不同设备间保持同步和最新).

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

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

了解基本知识

  • 什么是异步JavaScript?

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

  • JavaScript默认是异步的吗?

    No. 默认情况下, JavaScript是同步的, 单线程编程语言,其中指令一个接一个地运行,而不是并行运行. 异步技术并没有改变这一基本限制, 但它们确实有助于减少程序阻塞.

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

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

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

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

验证专家 在工程
10 的经验

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

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

作者简介

Muhammad是一名全栈开发人员,拥有10年为Resulta和其他大型营销公司提供web和移动应用程序的经验. 他拥有跨Node的JavaScript专业知识.js,下一个.js、React、React Native和WordPress解决方案.

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

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

订阅意味着同意我们的 隐私政策

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

订阅意味着同意我们的 隐私政策

Toptal开发者

加入总冠军® 社区.