什么是显式承诺构造反模式以及如何避免它?

2024-11-02 20:59:00
admin
原创
35
摘要:问题描述:我正在编写的代码执行以下操作:function getStuffDone(param) { return new Promise(function(resolve, reject) { myPromiseFn(param+1) .then(function...

问题描述:

我正在编写的代码执行以下操作:

function getStuffDone(param) {
    return new Promise(function(resolve, reject) {
        myPromiseFn(param+1)  
        .then(function(val) { 
            resolve(val);     
        }).catch(function(err) {
            reject(err);
        });
    });
}

或者:

显示代码片段

function getStuffDone(param) {
    var d = Q.defer(); /* or $q.defer */
    // or = new $.Deferred() etc.
    myPromiseFn(param+1)
    .then(function(val) { /* or .done */
        d.resolve(val);
    }).catch(function(err) { /* .fail */
        d.reject(err);
    });
    return d.promise; /* or promise() */
}

Run code snippetHide resultsExpand snippet

有人告诉我这分别被称为“延迟反模式”或“Promise构造函数反模式”,这段代码有什么不好,为什么称之为反模式?


解决方案 1:

Esailija提出的延迟反模式(现在是显式构造反模式)是初学 Promise 的人经常犯的一个反模式,我第一次使用 Promise 时也犯过这个错误。上述代码的问题在于,它没有利用 Promise 链式传递这一事实。

Promises 可以与 链接.then,并且您可以直接返回 Promises。您的代码getStuffDone可以重写为:

function getStuffDone(param){
    return myPromiseFn(param+1); // much nicer, right?
}

Promises 的作用是让异步代码更易读,并使其行为与同步代码相似,但又不隐藏这一事实。Promises 表示对一次性操作值的抽象,它们抽象了编程语言中语句或表达式的概念。

仅当您将 API 转换为承诺且无法自动执行时,或者当您编写以这种方式更容易表达的聚合函数时,才应该使用延迟对象。

引用 Esailija 的话:

这是最常见的反模式。如果你不真正理解 Promise,并认为它们是被美化的事件发射器或回调实用程序,就很容易陷入这种模式。让我们回顾一下:Promise 是关于让异步代码保留同步代码的大部分丢失属性,例如扁平缩进和一个异常通道。

解决方案 2:

它有什么问题?

但这个模式有效!

你真幸运。不幸的是,它可能没有,因为你可能忘记了一些极端情况。在我见过的超过一半的案例中,作者忘记了处理错误处理程序:

function bad() {
    return new Promise(function(resolve) {
        getOtherPromise().then(function(result) {
            resolve(result.property.example);
        });
    })
}

如果另一个承诺被拒绝,这将不会被注意,而不是传播到新的承诺(在那里它会被处理) - 并且新的承诺永远处于待处理状态,这可能会导致泄漏。

同样的事情也发生在回调代码导致错误的情况下 - 例如,当result没有property并且抛出异常时。这将得不到处理,导致新的承诺无法解决。

相比之下,使用.then()会自动处理这两种情况,并在发生错误时拒绝新的承诺:

function good() {
    return getOtherPromise().then(function(result) {
        return result.property.example;
    })
}

延迟反模式不仅麻烦,而且容易出错。使用.then()链式调用更安全。

但我已经处理好一切了!

真的吗?很好。但是,这将非常详细和丰富,特别是如果您使用支持其他功能(如取消或消息传递)的 Promise 库。或者也许将来会这样,或者您想将您的库换成更好的库?您不会想为此重写代码。

库的方法 ( then) 不仅原生支持所有功能,还可能进行某些优化。使用它们可能会让您的代码运行得更快,或者至少允许通过库的未来修订进行优化。

我该如何避免?

因此,每当您发现自己手动创建PromiseDeferred涉及已存在的承诺时,请先检查库 API。延迟反模式通常由那些将承诺 [仅] 视为观察者模式的人应用 - 但承诺不仅仅是回调:它们应该是可组合的。每个像样的库都有许多易于使用的函数,用于以各种可以想到的方式组合承诺,处理所有您不想处理的低级内容。

如果您发现需要以新的方式组合一些承诺,而现有辅助函数不支持这种方式,那么使用不可避免的 Deferred 编写自己的函数应该是您的最后选择。考虑切换到功能更强大的库,和/或针对当前库提交错误。它的维护者应该能够从现有函数中派生出组合,为您实现新的辅助函数和/或帮助识别需要处理的边缘情况。

解决方案 3:

七年后的今天,这个问题有了更简单的答案:

如何避免显式构造函数反模式?

使用async functions,然后await每个 Promise!

不需要手动构建像这样的嵌套 Promise 链:

function promised() {
  return new Promise(function(resolve) {
    getOtherPromise().then(function(result) {
      getAnotherPromise(result).then(function(result2) {
        resolve(result2);
      });
    });
  });
}

只需转动你的函数async并使用await关键字来停止函数的执行,直到 Promise 解析为止:

async function promised() {
   const result =  await getOtherPromise();
   const result2 = await getAnotherPromise(result);
   return result2;
}

这有多种好处:

  • 调用该async函数始终返回一个 Promise,该 Promise 使用返回值解析,如果异步函数内部抛出错误则拒绝

  • 如果awaited Promise 拒绝,错误就会在异步函数内部引发,因此您可以像处理try { ... } catch(error) { ... }同步错误一样处理它。

  • 你可以await在循环和 if 分支中使用它,这样就可以简化大部分 Promise 链逻辑

  • 尽管异步函数的行为与 Promises 链非常相似,但它们更易于阅读(也更容易推理)

我如何await回电?

如果回调仅回调一次,并且您调用的 API 尚未提供 Promise(大多数都提供!)这是使用 Promise 构造函数的唯一原因:

 // Create a wrapper around the "old" function taking a callback, passing the 'resolve' function as callback
 const delay = time => new Promise((resolve, reject) =>
   setTimeout(resolve, time)
 ); 

 await delay(1000);

如果await停止执行,调用是否async function直接返回结果?

不可以。如果您调用异步函数,则始终会返回 Promise。然后您也可以await在异步函数内返回该 Promise。您无法在同步函数内等待结果(您必须调用.then并附加回调)。

从概念上讲,同步function总是在一项作业中运行至完成,而 则async function同步运行直至达到await,然后它们在另一项作业中继续运行。

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用