JavaScript 回调中的控制反转:为什么 Promise 是答案

回调函数是作为参数传递给另一个函数的函数,然后在外部函数内部调用该函数以完成某种例程或操作。可以通过两种方式调用回调:同步和异步。这是 javascript 中最基本的异步模式。

例如:

JavaScript 回调中的控制反转:为什么 Promise 是答案

AB现在发生,在主JS程序的直接控制下。但是 C 被推迟到稍后发生,并在另一方的控制下,在本例中是 ajax(..) 函数。从基本意义上来说,这种控制权的交接通常不会给程序带来很多问题。

但是,频率不高并不足以忽视问题或问题。事实上,这是回调驱动设计的主要问题之一。它围绕着这样一个想法:有时 ajax(..) 或您传递回调延续的“一方”不是您编写的函数,也不是您直接控制的函数。很多时候它是一些第三方提供的实用程序。

立即学习“Java免费学习笔记(深入)”;

当您参与程序并将其执行控制权交给另一个第三方时,我们称之为“控制反转”。您的代码和第三方实用程序之间存在一个不言而喻的“合同”——您希望维护的一组内容。

“控制反转”有什么问题?

有一个例子可以更好地理解这个问题。

假设您正在为您的公司建立一个旅行预订网站。您添加了一个使用第三方库的 createBooking() 函数的“立即预订”按钮。此函数处理预订,然后调用您的回调以向客户发送确认电子邮件。

这段代码可能看起来像:

JavaScript 回调中的控制反转:为什么 Promise 是答案

测试期间一切正常。人们开始在您的网站上预订旅行套餐,每个人都对您的工作感到满意。突然有一天,你接到老板打来的电话,询问一个重大问题。一位顾客预订了套餐,一次预订收到了四封相同的确认电子邮件。

您开始调试问题。您查看发送确认电子邮件的代码部分,一切似乎都是正确的。然后,您进一步调查,发现第三方库中的 createBooking() 实用程序调用了您的回调函数四次,导致发送了四封确认电子邮件。

您联系第三方图书馆的支持团队并说明情况。他们告诉您,他们以前从未遇到过这个问题,但会优先考虑该问题并回复您。一天后,他们会给您回电话并告知他们的调查结果。他们发现一段不应该上线的实验性代码导致 createBooking() 函数多次调用回调。

问题是在他们这边,他们向你保证它已经解决了。他们对造成的麻烦表示歉意,并确认该问题不会再次发生。

为了防止在寻求解决方案后出现任何不需要的问题,您可以实现一个简单的 if 语句,如下所示,团队对此似乎很满意:

JavaScript 回调中的控制反转:为什么 Promise 是答案

但是随后一位 QA 工程师问道:“如果他们从不调用回调会发生什么?”哎呀。你们都没有想过这一点!

您开始考虑他们调用您的回调可能出现的所有问题。以下是一些问题的列表:

过早调用回调

调用回调太晚了(或根本不调用)

调用回调的次数太少或太多(如上例中的问题)

吞下任何可能发生的错误/异常

您可能会意识到,您必须在代码中针对不同情况实现许多解决方案,这将使代码变得糟糕且肮脏,因为传递给实用程序的每个回调都需要它。

承诺

如果我们能够反转控制反转会怎样?如果我们不是将程序的延续传递给另一方,而是期望它返回给我们一种能力,让我们知道其任务何时完成,然后我们的代码可以决定下一步做什么?

Promises 提供了一种强大的方法来处理 JavaScript 中的异步操作,解决回调地狱和控制反转等问题。与回调不同,Promises 允许您管理异步任务,而无需放弃对代码的控制。

考虑在快餐店点一个芝士汉堡。您会收到一张收据,这是对您未来的芝士汉堡的承诺。在等待的同时,您可以做其他事情,因为您知道最终会收到订单。同样,JavaScript 中的 Promise 代表了一个未来的值,让你的代码能够顺利进行。

Promise 也能优雅地处理失败,就像餐厅的芝士汉堡卖完时你可能会收到通知一样。这种结构使您的代码更具可读性和可维护性。

我不会在这里深入探讨 Promise,但是通过使用它们,您可以编写更干净、更可靠的异步代码,从而提高 JavaScript 应用程序的整体质量。

例如,对于我们之前描述的旅行预订网站,如果第三方实用程序返回一个承诺,我们可以这样处理:

JavaScript 回调中的控制反转:为什么 Promise 是答案

一旦 Promise 被解决(成功完成),它就会永远保持这种状态——此时它就成为一个不可变的值,然后可以根据需要多次观察。这意味着已解决的承诺,其已解决的值可以在代码中多次访问或使用,而不会影响其状态。这使您可以在应用程序的各个部分处理异步操作的结果,而不必担心 Promise 更改或重新评估。

Promises 是针对仅回调代码中发生的控制反转问题的解决方案。回调代表控制反转。所以反转回调模式实际上是反转的反转,或者说控制的反反转。将控制权恢复到我们最初希望的调用代码。

参考

你不懂 JS:凯尔·辛普森 (Kyle Simpson) 的异步与性能

developer.mozilla.org

以上就是JavaScript 回调中的控制反转:为什么 Promise 是答案的详细内容,更多请关注创想鸟其它相关文章!

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

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月19日 13:00:27
下一篇 2025年12月19日 13:00:37

相关推荐

  • 使用 jQuery 和 Select2 获取所选值

    第一段引用上面的摘要: 本文档介绍了如何使用 jQuery 和 Select2 插件获取多选下拉框中所选的值。我们将演示如何初始化 Select2,并提供代码示例,展示如何通过监听 change 事件来实时获取所选值的数组。掌握这些方法,你将能够轻松地在你的 Web 应用中集成 Select2 并获…

    好文分享 2025年12月20日
    000
  • 实现表单在特定DIV内提交而不刷新整个页面的方法

    本文旨在提供一种解决方案,实现在不刷新整个页面的情况下,将表单提交到页面中特定的 元素内。我们将探讨使用 JavaScript 拦截表单提交事件,通过 Ajax 将表单数据发送到服务器,并将服务器返回的结果更新到指定的 区域,从而实现局部更新的效果。 在Web开发中,有时我们需要在不刷新整个页面的情…

    2025年12月20日
    000
  • 如何在不刷新整个页面的情况下,将表单提交到指定DIV区域?

    本文旨在解决如何将HTML表单提交到页面上的特定 区域,而无需刷新整个页面的问题。我们将探讨使用 一种方法是将目标 替换为 优点: 实现简单,易于理解。不需要编写 JavaScript 代码。 缺点: 引入额外的 HTML 文档,可能会增加服务器的负担。 方法二:使用 Ajax 更常用的方法是使用 …

    2025年12月20日
    000
  • 如何在不刷新整个页面的情况下,将表单提交到指定DIV容器内

    本文介绍了如何在不刷新整个页面的情况下,将位于特定 容器内的表单提交到该容器内。主要探讨了使用 以上就是如何在不刷新整个页面的情况下,将表单提交到指定DIV容器内的详细内容,更多请关注创想鸟其它相关文章!

    2025年12月20日
    000
  • 如何在不刷新整个页面的情况下,将表单提交到特定 DIV 中

    本文旨在解决如何将表单提交到页面上的特定 元素中,而无需刷新整个页面。我们将探讨使用 一种方法是将目标 替换为 缺点: 方法二:使用 AJAX 拦截表单提交 更灵活的方法是使用 JavaScript 拦截表单提交,然后使用 AJAX 将表单数据发送到服务器,并将响应更新到目标 中。 步骤: 拦截表单…

    2025年12月20日
    000
  • 使用 useEffect 获取数据时,API 工具函数无法正确更新状态的解决方案

    第一段引用上面的摘要: 本文针对 React 初学者在使用 useEffect 钩子获取数据并使用工具函数进行 API 调用时,遇到的数据无法正确更新状态的问题,提供了详细的分析和解决方案。通过修改 API 工具函数,确保 fetch 调用返回 Promise,从而保证数据能够正确传递并更新组件状态…

    2025年12月20日
    000
  • 如何在不刷新整个页面的情况下,将表单提交到指定DIV中

    本文旨在解决如何将HTML表单提交到页面上的特定 容器内,而避免刷新整个页面的问题。我们将探讨使用 以上就是如何在不刷新整个页面的情况下,将表单提交到指定DIV中的详细内容,更多请关注创想鸟其它相关文章!

    2025年12月20日
    000
  • JavaScript石头剪刀布游戏:计分与逻辑优化教程

    本文旨在指导开发者使用 JavaScript 实现一个简单的石头剪刀布游戏,并重点解决计分问题和优化游戏逻辑。我们将通过示例代码,详细讲解如何正确地跟踪玩家和电脑的得分,并提供一种更简洁的方式来判断胜负,提升代码的可读性和效率。 游戏初始化与用户输入 首先,我们需要定义游戏所需的变量,包括可选的选项…

    2025年12月20日
    000
  • JavaScript 猜拳游戏:完善计分与逻辑优化教程

    本文旨在帮助开发者构建一个基于浏览器的 JavaScript 猜拳游戏,并解决计分逻辑和简化游戏判断的问题。我们将逐步优化代码,提供更清晰的结构和更简洁的实现方式,确保游戏逻辑的正确性和可维护性。最终,你将拥有一个功能完善、易于理解的猜拳游戏。 游戏核心逻辑实现 首先,我们定义游戏选项,并初始化玩家…

    2025年12月20日
    000
  • JavaScript 猜拳游戏:完善你的计分系统与逻辑

    本文将引导你构建一个基于 JavaScript 的猜拳游戏,重点解决计分逻辑问题,并提供更简洁高效的实现方案。我们将深入探讨如何使用数组索引和模运算来简化胜负判断,同时优化用户输入验证,确保游戏的健壮性和用户体验。通过本文,你将掌握编写清晰、可维护的 JavaScript 代码的技巧,并提升解决实际…

    2025年12月20日
    000
  • React useEffect 数据获取问题:API 调用返回值处理详解

    本文针对 React 初学者在使用 useEffect 进行数据获取时遇到的 setThings 未能正确更新状态的问题,进行了深入分析和详细解答。通过剖析 ThingsAPI.getAll 方法中 Promise 返回值的处理方式,指出了问题的根源在于缺少 return 语句,导致异步操作未正确完…

    2025年12月20日
    000
  • JavaScript 猜拳游戏:完善计分与逻辑优化

    本文旨在帮助开发者构建一个基于浏览器的 JavaScript 猜拳游戏,并解决计分逻辑问题。我们将提供清晰的代码示例,并深入探讨如何使用数组索引和模运算来简化胜负判断。通过本文,你将掌握如何编写一个功能完善、逻辑清晰的猜拳游戏。 游戏结构与核心逻辑 一个简单的猜拳游戏通常包含以下几个核心部分: 获取…

    2025年12月20日
    000
  • 使用 Slim Select 选择加密货币后显示价格的教程

    本教程旨在指导开发者在使用 Slim Select 插件的 Rails 应用中,如何实现选择加密货币名称后,自动从数据库获取并显示其价格的功能。通过 AJAX 请求,我们可以动态更新页面,提供更友好的用户体验。本教程将涵盖前端 CoffeeScript 代码的编写以及后端 Rails 控制器的实现。…

    2025年12月20日
    000
  • 根据 Slim Select 选择的加密货币名称显示价格

    本文将指导你如何在 Rails 应用中使用 Slim Select 库,实现根据用户选择的加密货币名称,动态地从数据库获取并显示其价格。我们将通过 CoffeeScript 和 Rails 后端代码的结合,展示如何使用 AJAX 技术实现这一功能,并提供详细的代码示例和步骤说明。 前端实现:Coff…

    2025年12月20日
    000
  • 什么是单例模式?单例的实现方式

    单例模式确保一个类只有一个实例并提供全局访问点,适用于资源管理、配置管理等场景,常见实现方式包括饿汉式、懒汉式、双重检查锁、静态内部类和枚举,其中静态内部类和枚举因线程安全且实现简洁更受推荐。 单例模式确保一个类只有一个实例,并提供一个全局访问点。这在管理共享资源、配置对象等方面非常有用。 解决方案…

    2025年12月20日
    000
  • 什么是二叉堆?二叉堆的插入和删除

    二叉堆是一种用数组实现的完全二叉树,满足堆属性,分为最小堆和最大堆,能高效插入、删除并获取最值,时间复杂度为O(log N);其核心操作为插入时的“上浮”和删除堆顶时的“下沉”;常见应用包括优先队列、堆排序、Dijkstra与Prim算法及Top K问题。 二叉堆本质上是一种特殊的完全二叉树,它满足…

    2025年12月20日
    000
  • 什么是虚拟DOM?虚拟DOM的Diff

    虚拟DOM是真实DOM的轻量级JavaScript副本,核心目的是优化频繁DOM操作的性能。它通过在内存中进行计算,利用Diff算法比较新旧虚拟DOM树,找出最小差异并生成补丁,最后批量更新真实DOM,减少重排和重绘。Diff算法基于同层比较、节点类型判断、属性对比和key机制,实现高效更新。同步时…

    2025年12月20日
    000
  • JavaScript异步操作进阶:高效管理并发Promise与forEach陷阱

    在JavaScript中,处理并发异步操作时,forEach循环与async/await的组合常会导致意想不到的行为,因为forEach不会等待其回调函数中的异步操作完成。本文将深入探讨这一常见陷阱,解释其发生原因,并提供使用Promise.all结合map的健壮解决方案,以确保所有并发Promis…

    2025年12月20日
    000
  • js怎么删除原型链上的属性

    js中删除原型链上的属性,答案是可以使用delete操作符直接删除,但强烈不建议这样做,因为这会影响所有继承该原型的实例并可能引发难以追踪的bug;1. 可以通过delete myobject.prototype.propname删除原型上的属性,使其对所有实例不可访问;2. 不建议这样做的原因是它…

    2025年12月20日 好文分享
    000
  • 什么是顺序查找?顺序查找的实现

    顺序查找是从头到尾逐个比对元素的查找方法,时间复杂度为O(n),适用于数据量小、无序或查找频率低的场景,可通过将高频元素前置或使用哨兵优化,但效率低于二分查找和哈希查找。 顺序查找,也叫线性查找,说白了就是从头到尾一个一个比对,直到找到你想要的,或者找遍了都没找到。简单粗暴,但有时候也挺管用。 顺序…

    2025年12月20日
    000

发表回复

登录后才能评论
关注微信