使用 async/await 实现条件等待:一种轮询机制

使用 async/await 实现条件等待:一种轮询机制

本文探讨了如何在 JavaScript 中利用 `async/await` 机制,实现等待特定条件满足后才继续执行后续代码的功能。通过构建一个基于轮询的 `busyWait` 函数,结合 `setTimeout` 和 `Promise`,我们能够模拟出 `await(condition)` 的效果,有效管理异步操作中的条件依赖。文章详细阐述了实现原理、代码示例及使用时的注意事项。

在异步编程中,我们经常会遇到需要等待某个条件变为真(例如,等待数据加载完成、某个状态更新、或特定资源可用)才能继续执行后续逻辑的场景。虽然 await 关键字主要用于等待 Promise 对象的解决,但它本身不能直接用于等待一个布尔表达式为真,例如 await(x === true) 这样的语法是不被支持的。然而,我们可以巧妙地结合 async/await、Promise 和 setTimeout 来实现一个自定义的条件等待机制,即通过轮询(Polling)来检查条件。

实现原理:基于轮询的条件等待

核心思想是创建一个异步函数,它在一个循环中反复检查某个条件。如果条件不满足,它就暂停一小段时间(通过 setTimeout 和 Promise 实现),然后再次检查。直到条件满足,循环才终止,函数执行完毕,从而允许 await 它的调用者继续执行。

我们将这个机制封装在一个名为 busyWait 的函数中。

async function busyWait(testCondition) {  const delayMs = 500; // 每次检查的间隔时间(毫秒)  // 循环检查条件,直到 testCondition() 返回 true  while (!testCondition()) {    // 使用 await 暂停执行 delayMs 毫秒    await new Promise(resolve => setTimeout(resolve, delayMs));  }}

函数解析:

testCondition: 这是一个回调函数,它封装了我们想要等待的条件。busyWait 函数会反复调用这个函数来检查条件是否满足。delayMs: 定义了每次条件检查之间的等待间隔。这个值决定了轮询的频率和应速度。while (!testCondition()): 这是轮询的核心。只要 testCondition() 返回 false(即条件不满足),循环就会继续。await new Promise(resolve => setTimeout(resolve, delayMs)): 这是实现暂停的关键。new Promise(resolve => …) 创建了一个新的 Promise。setTimeout(resolve, delayMs) 在 delayMs 毫秒后调用 resolve 函数。await 关键字会等待这个 Promise 解决,这意味着当前的 async 函数会在这里暂停执行 delayMs 毫秒,然后才会继续到 while 循环的下一次迭代,重新检查条件。

示例代码

让我们通过一个具体的例子来演示 busyWait 函数的用法。假设我们有一个变量 a,它在一段时间后会被异步修改,我们希望在 a 变为特定值后才执行后续操作。

console.log('begin');// busyWait 函数的实现(如上所示)async function busyWait(testCondition) {  const delayMs = 500;  while (!testCondition()) {    await new Promise(resolve => setTimeout(resolve, delayMs));  }}let a = 'hello'; // 初始值// 模拟异步操作:2秒后将 a 的值改为 'world'setTimeout(() => {  a = 'world';  console.log('Variable "a" changed to "world"');}, 2000);// 使用立即执行的 async 函数来演示 busyWait(async () => {  console.log('Waiting for "a" to be "world"...');  // 等待直到变量 a 的值变为 'world'  await busyWait(() => a === 'world');  console.log('Condition met! Variable "a" is now "world".');  console.log('done');})();

运行流程:

console.log(‘begin’) 首先输出。a 被初始化为 ‘hello’。setTimeout 被设置,它将在 2000 毫秒(2秒)后执行,将 a 的值更新为 ‘world’。立即执行的 async 函数开始执行。console.log(‘Waiting for “a” to be “world”…’) 输出。await busyWait(() => a === ‘world’) 被调用。busyWait 函数内部开始循环检查 a === ‘world’。在最初的 2 秒内,a 仍然是 ‘hello’,所以条件为 false。每次检查失败,busyWait 都会暂停 500 毫秒。大约 2 秒后,setTimeout 回调执行,a 的值变为 ‘world’。busyWait 的下一次检查发现 a === ‘world’ 为 true。busyWait 循环终止,Promise 解决,await busyWait(…) 完成。console.log(‘Condition met! Variable “a” is now “world”.’) 输出。console.log(‘done’) 输出。

这个例子清晰地展示了 busyWait 如何在不阻塞主线程的情况下,有效地等待一个异步条件。

注意事项与最佳实践

轮询间隔 (delayMs) 的选择:

过短: 会导致频繁检查,增加 CPU 负载,尤其是在条件长时间不满足的情况下。过长: 会增加条件满足后的响应时间。选择一个合适的 delayMs 需要权衡响应速度和资源消耗。对于大多数前端或轻量级后端场景,几百毫秒(如 100ms – 500ms)通常是一个合理的范围。

避免无限循环:

如果 testCondition 永远不会变为 true,busyWait 将会进入无限循环,导致程序卡死。解决方案: 引入一个超时机制。可以在 busyWait 函数中添加一个最大等待时间参数,如果超过这个时间条件仍未满足,则抛出错误或返回一个特定的值。

async function busyWaitWithTimeout(testCondition, timeoutMs, delayMs = 500) {  const startTime = Date.now();  while (!testCondition()) {    if (Date.now() - startTime > timeoutMs) {      throw new Error('Timeout: Condition not met within specified time.');    }    await new Promise(resolve => setTimeout(resolve, delayMs));  }}// 使用示例:// await busyWaitWithTimeout(() => a === 'world', 5000); // 最多等待5秒

替代方案:事件驱动或响应式编程:

虽然轮询是一个通用的解决方案,但在某些场景下,事件驱动或响应式编程模式可能更高效。如果条件的变化可以通过事件(例如 DOM 事件、WebSocket 消息、Node.js EventEmitter)来通知,那么直接监听事件并执行回调会比轮询更即时、更节省资源。如果涉及到复杂的状态管理和数据流,RxJS 等响应式编程库可以提供更强大的工具来处理异步条件和数据流。然而,对于简单的条件等待,或者在没有现成事件机制的情况下,busyWait 提供了一个简洁有效的 async/await 解决方案。

总结

通过 async/await 结合 Promise 和 setTimeout 实现的 busyWait 函数,为 JavaScript 开发者提供了一种灵活且易于理解的条件等待机制。它允许程序在不阻塞主线程的前提下,暂停执行直到某个条件满足。在使用时,合理设置轮询间隔并考虑添加超时机制,可以确保代码的健壮性和性能。虽然存在更高级的异步模式,但对于许多场景,这种基于轮询的 await 条件等待方法是一个实用且直接的解决方案。

以上就是使用 async/await 实现条件等待:一种轮询机制的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/198092.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年11月1日 19:32:35
下一篇 2025年11月1日 19:37:37

相关推荐

  • 为什么服务端程序退出后端口还会被占用?

    端口占用背后的原因 当 tcp 服务端程序退出时,端口可能会继续被占用,这是因为未正确地处理连接释放。当客户端连接到服务器时,操作系统会创建一个套接字,用于在客户端和服务器之间交换数据。 服务器程序退出时,它会关闭其套接字,但它可能没有正确地断开与客户端的连接。在这种情况下,客户端仍然认为与服务器的…

    2025年12月13日
    000
  • TCP 服务端退出后端口被占用怎么办?

    tcp 服务端退出后端口仍被占用原因及其解决方法 当服务端程序意外退出时,可能导致端口被 time_wait 状态占用。这是一个正常的系统行为,旨在确保已连接的套接字被正确释放。然而,在某些情况下,这可能会导致再次启动服务端时出现“地址已使用”的错误。 为了解决此问题,可以在侦听端口前设置 so_r…

    2025年12月13日
    000
  • TCP 服务端程序退出后,为什么端口还会被占用?

    为什么 tcp 服务端程序退出后,端口仍然被占用? 当 tcp 服务端程序意外退出时,可能会导致端口仍然被占用。这是因为在 linux 系统中,已连接的套接字在程序退出后不会自动释放,而是会进入一个名为 time_wait 的状态。 during this period, the socket re…

    2025年12月13日
    000
  • Web 开发人员的 Python 入门

    python 已成为最流行的编程语言之一,广泛应用于 web 开发、数据分析、人工智能等领域。对于 web 开发人员来说,python 提供了强大的框架、简单的语法和充满活力的库生态系统。如果您是一名希望扩展到后端开发的 web 开发人员,python 是一个绝佳的选择。以下是如何开始。 作为 we…

    2025年12月13日
    000
  • Go语言匿名函数闭包中如何解决函数值相同问题?

    go语言晚绑定与匿名函数闭包 在go语言中,匿名函数闭包是一个常见的概念。然而,在创建数组并填充匿名函数时,可能会面临函数值相同的闭包问题。 当创建一个包含void -> int类型函数的空数组时,每个匿名函数的值都会相同,这是因为循环变量i在离开循环块后就无法访问。这与许多编译型语言一致。 …

    2025年12月13日
    000
  • pytest 运行输出中的 E s 代表什么?

    理解 pytest 运行输出中的 E s . pytest 是一款用于 Python 测试的流行测试框架。当运行 pytest 时,它会在控制台输出一个报告,其中包含有关测试用例结果的信息。其中,可能会出现 E s . 这些符号表示不同的测试结果: . (点号):表示用例通过。F (Failure)…

    2025年12月13日
    000
  • 云VPS托管:优化Python开发环境的关键

    在不断发展的软件开发世界中,Python 已成为最流行、最通用的编程语言之一。从 Web 开发和数据科学到自动化和人工智能,Python 丰富的库和框架生态系统使其成为开发人员的绝佳选择。然而,对于 Python 开发人员来说,要充分发挥该语言的潜力,拥有可靠且高效的开发环境至关重要。这就是云 VP…

    2025年12月13日
    000
  • FastAPI 中如何解析用逗号分隔的多个 Query 参数?

    在fastapi中,默认情况下,同名query参数会被合并成一个列表。例如,请求?source=manual&source=vdna将得到source=[‘manual’, ‘vdna’]。 如果你希望使用逗号分隔多个query参数的值,有两种…

    2025年12月13日
    000
  • torch-tensorrt 安装报错:如何正确使用 pip 进行安装?

    安装 torch-tensorrt 报错? 在尝试使用 pip 安装 torch-tensorrt 时,遇到了以下错误: error: metadata-generation-failed× encountered error while generating package metadata.╰─…

    2025年12月13日
    000
  • 如何在启用 Python 虚拟环境 (venv) 的项目中编写 .gitignore?

    如何在启用了虚拟环境 (venv) 的 python 项目中编写 .gitignore 在使用 python 虚拟环境 (venv) 管理项目时,往往会遇到 .gitignore 文件的编写问题,尤其是当项目中包含了 flask 框架。这里将介绍如何在启用 venv 的情况下编写 .gitignor…

    2025年12月13日
    000
  • FastAPI 中如何将逗号分隔的字符串解析为列表?

    fastapi 中如何使用逗号分割 list,而不是同名键值对? fastapi 通常将 query 参数解析为列表,当同名参数多次出现时。然而,在某些情况下,你可能更喜欢使用逗号分隔,生成单个字符串值。 解决方案: 有两种方法可以做到这一点: 1. 使用字符串参数 将 query 参数声明为字符串…

    2025年12月13日
    000
  • Python 项目容器如何自动激活虚拟环境?

    python 项目容器自动激活虚拟环境? 在首次部署 python 容器后,您可能会遇到每次进入容器时都需要手动激活虚拟环境的问题。为了解决此问题,您可能倾向于寻找一种方法,使每次执行 docker exec 时都能自动激活它。 然而,使用虚拟环境的目的是为不同的项目隔离 python 依赖项。在容…

    2025年12月13日
    000
  • 为什么 Python 在科学领域比 JavaScript 更受欢迎?

    为什么 Python 在科学领域如此盛行? 尽管 JavaScript 在 Web 开发中主宰地位,但 Python 却在科学领域备受推崇。这背后的原因并非 JavaScript 缺乏库所致,而是因为它的语言特性不适合这种高度精确计算的要求。 JavaScript 的局限性: 单线程:JavaScr…

    2025年12月13日
    000
  • Go 语言中数组函数的晚绑定:为什么所有函数都返回 5?

    go 语言中的晚绑定 在编程中,我们经常需要处理数组中的函数。在 go 中,对数组中的函数进行晚绑定会带来一些有趣的情况。 import “fmt”type intorfunctype intfunc main() { var fns [5]intorfunctype for i := 0; i &…

    2025年12月13日
    000
  • 使用 GemBatch 降低提示链接的成本

    提示链正在彻底改变我们与大型语言模型 (llm) 交互的方式。通过将多个提示链接在一起,我们可以创建复杂、动态的对话并处理复杂的任务。但这种力量确实是有代价的。每次对 llm 服务(例如 google 的 gemini)的 api 调用都会增加您的账单。 许多llm提供商提供了一个解决方案:批处理。…

    2025年12月13日
    000
  • 利用 OpenTelemetry 增强机器学习的可观测性:InsightfulAI 更新

    介绍 在机器学习领域,可观察性经常被忽视,但它对于维护稳健、性能良好的模型至关重要。今天,我们很高兴地宣布 InsightfulAI 现在完全支持 OpenTelemetry!这种集成为开发人员提供了用于监控、跟踪和排除机器学习工作流程故障的强大工具。以下是 InsightfulAI 现在与 Ope…

    2025年12月13日
    000
  • Pytest 输出 E s . 代表了什么?

    解读 Pytest 输出状态代码 在运行 Pytest 测试时,你会看到不同的状态代码输出,例如 E s . 等。这些代码表示测试用例的执行结果。 .(点号):测试用例通过。F(Failure):测试用例失败。E(Error):测试用例中出现异常。S(Skip):测试用例被跳过。x(小写 x):预期…

    2025年12月13日
    000
  • 如何使用 Python 多线程处理列表中字典参数?

    多线程处理列表中字典参数 针对需要使用多线程传递字典参数执行函数的情况,我们可以利用python提供的threadpoolexecutor线程池高效地完成此任务。 实现步骤 导入 nécessaire 库: import timefrom concurrent.futures import thre…

    2025年12月13日
    000
  • pytest 输出标识的含义:如何解读测试结果中的符号?

    pytest 输出标识的含义 在运行 pytest 时,输出结果中可能包含各种字母符号,表示测试用例的状态。 以下是这些符号的含义: .: 点号,表示用例通过。F: 表示失败(Failure)。E: 表示用例中存在异常(Error)。S: 表示用例被跳过(Skip)。x: 小写的 x 表示预期失败(…

    2025年12月13日
    000
  • pytest 输出中的“E s .”分别代表什么?

    pytest 输出中的“E s .”含义 在运行 pytest 测试时,您可能会在输出中看到“E s .”字符。这些字符表示测试运行的结果: 点号(.):表示测试用例通过。F:表示测试用例失败(Failure)。E:表示测试用例中发生了异常(Error)。S:表示测试用例已被跳过(Skip)。小写的…

    2025年12月13日
    000

发表回复

登录后才能评论
关注微信