什么是明确的承诺建筑对抗,我该如何避免?

发布于 2025-01-23 10:28:25 字数 877 浏览 4 评论 0 原文

我正在写代码,这些代码看起来像:

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() */
}

有人告诉我,这被称为“ 递延的对抗”或“分别称为 antipattern

I was writing code that does something that looks like:

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

Or:

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() */
}

Someone told me this is called the "deferred antipattern" or the "Promise constructor antipattern" respectively, what's bad about this code and why is this called an antipattern?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

最偏执的依靠 2025-01-30 10:28:25

esailija 是一个新手的承诺,我做出了当我第一次使用诺言时,我自己。上述代码的问题在于未能利用承诺链的事实。

承诺可以用链条。然后,您可以直接返回承诺。您的代码 getStuffDone 可以被重写为:

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

承诺是关于使异步代码更可读取,并且在不隐藏该事实的情况下表现得像同步代码。承诺代表了一次性操作值的抽象,他们在编程语言中抽象了语句或表达的概念。

您只应在,不能自动做到这一点,或者当您编写更容易表达的聚合功能时。

引用Esailija:

这是最常见的抗模式。当您不真正理解承诺并将它们视为荣耀的活动发射器或回调实用程序时,很容易陷入困境。让我们回顾一下:承诺是关于使异步代码保留同步代码的大部分丢失属性,例如平面凹痕和一个例外通道。

The deferred antipattern (now explicit-construction anti-pattern) coined by Esailija is a common anti-pattern people who are new to promises make, I've made it myself when I first used promises. The problem with the above code is that is fails to utilize the fact that promises chain.

Promises can chain with .then and you can return promises directly. Your code in getStuffDone can be rewritten as:

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

Promises are all about making asynchronous code more readable and behave like synchronous code without hiding that fact. Promises represent an abstraction over a value of one time operation, they abstract the notion of a statement or expression in a programming language.

You should only use deferred objects when you are converting an API to promises and can't do it automatically, or when you're writing aggregation functions that are easier expressed this way.

Quoting Esailija:

This is the most common anti-pattern. It is easy to fall into this when you don't really understand promises and think of them as glorified event emitters or callback utility. Let's recap: promises are about making asynchronous code retain most of the lost properties of synchronous code such as flat indentation and one exception channel.

后来的我们 2025-01-30 10:28:25

怎么了?

但是图案有效!

幸运的你。不幸的是,它可能没有,因为您可能忘记了一些边缘案例。在我看过的一半以上的事件中,作者忘记了照顾错误处理程序:

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

如果拒绝了另一个诺言,这将是没有注意到的,而不是传播给新的诺言(在那里它将被处理) - 新的承诺将永远存在,这可能会导致泄漏。

如果您的回调代码会导致错误,例如结果没有属性,并且会引发异常。那将是没有帮助的,没有解决新的诺言。

相反,使用。然后()确实会自动照顾这两种情况,并在发生错误时拒绝新的承诺:

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

递延的attipattern不仅繁琐。使用

但是我已经处理了一切!

真的吗?好的。但是,这将非常详细且丰富,尤其是如果您使用支持取消或消息传递等其他功能的承诺库。或者,也许将来会,或者您想将图书馆与更好的图书馆交换?您不想为此重写代码。

库的方法(<代码>然后)不仅在本来支持所有功能,而且还可能具有某些优化。使用它们可能会使您的代码更快,或者至少可以通过库的未来修订来优化代码。

我如何避免它?

因此,每当您发现自己手动创建 Promise 延期并涉及现有的承诺时,检查库API首先。递延的反对者通常是由将承诺[仅]视为观察者模式的人所应用的 - 但是承诺是更多更多而不是回调:它们应该是可以组合的。每个体面的图书馆都有许多易于使用的功能,以每种可思考的方式构成承诺的构成,从而照顾您不想处理的所有低级内容。

如果您发现需要以一种新的方式撰写一些诺言,而现有助手功能不支持,则以不可避免的延期编写自己的功能应该是您的最后选择。考虑切换到更富含功能的库,和/或针对您当前库提交错误。它的维护者应该能够从现有功能中得出组成,为您实现新的辅助功能和/或帮助确定需要处理的边缘案例。

What's wrong with it?

But the pattern works!

Lucky you. Unfortunately, it probably doesn't, as you likely forgot some edge case. In more than half of the occurrences I've seen, the author has forgotten to take care of the error handler:

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

If the other promise is rejected, this will happen unnoticed instead of being propagated to the new promise (where it would get handled) - and the new promise stays forever pending, which can induce leaks.

The same thing happens in the case that your callback code causes an error - e.g. when result doesn't have a property and an exception is thrown. That would go unhandled and leave the new promise unresolved.

In contrast, using .then() does automatically take care of both these scenarios, and rejects the new promise when an error happens:

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

The deferred antipattern is not only cumbersome, but also error-prone. Using .then() for chaining is much safer.

But I've handled everything!

Really? Good. However, this will be pretty detailed and copious, especially if you use a promise library that supports other features like cancellation or message passing. Or maybe it will in the future, or you want to swap your library against a better one? You won't want to rewrite your code for that.

The libraries' methods (then) do not only natively support all the features, they also might have certain optimisations in place. Using them will likely make your code faster, or at least allow to be optimised by future revisions of the library.

How do I avoid it?

So whenever you find yourself manually creating a Promise or Deferred and already existing promises are involved, check the library API first. The Deferred antipattern is often applied by people who see promises [only] as an observer pattern - but promises are more than callbacks: they are supposed to be composable. Every decent library has lots of easy-to-use functions for the composition of promises in every thinkable manner, taking care of all the low-level stuff you don't want to deal with.

If you have found a need to compose some promises in a new way that is not supported by an existing helper function, writing your own function with unavoidable Deferreds should be your last option. Consider switching to a more featureful library, and/or file a bug against your current library. Its maintainer should be able to derive the composition from existing functions, implement a new helper function for you and/or help to identify the edge cases that need to be handled.

往日 2025-01-30 10:28:25

现在7年后,对此问题有一个更简单的答案:

我如何避免显式构造函数antpattern?

使用 async函数 s,然后等待 每个诺言!

而不是手动构造诸如此类的嵌套承诺链:

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

只需转动函数 async 并使用等待关键字 停止执行函数承诺决心:

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

这有各种好处:

  • 调用 async 函数始终返回承诺,如果
  • 等待承诺拒绝,错误会在异步函数中丢弃,因此您可以尝试{...} catch(error){...} 它像同步错误一样。
  • 您可以等待内部循环和如果分支机构,那么大多数承诺链逻辑琐事
  • 尽管异步功能的行为主要像诺言的链条,但它们更容易阅读(并且更易于推理)

)我如何等待回调?

如果回调只回电一次,而您正在调用的API也没有提供承诺(其中大多数是这样!)这是唯一的原因使用承诺构造函数:

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

如果等待停止执行,请调用 async函数直接返回结果?

否如果您调用异步功能,请始终返回承诺。然后,您可以等待在异步函数中也承诺。您迫不及待地等待同步函数内部的结果(您必须调用。然后并附加回调)。

从概念上讲,同步函数 s始终在一个作业中完成,而 async函数 s同步运行,直到它们达到等待,然后他们继续进入另一个工作。

Now 7 years later there is a simpler answer to this question:

How do I avoid the explicit constructor antipattern?

Use async functions, then await every Promise!

Instead of manually constructing nested Promise chains such as this one:

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

just turn your function async and use the await keyword to stop execution of the function until the Promise resolves:

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

This has various benefits:

  • Calling the async function always returns a Promise, which resolves with the returned value and rejects if an error get's thrown inside the async function
  • If an awaited Promise rejects, the error get's thrown inside the async function, so you can just try { ... } catch(error) { ... } it like the synchronous errors.
  • You can await inside loops and if branches, making most of the Promise chain logic trivial
  • Although async functions behave mostly like chains of Promises, they are way easier to read (and easier to reason about)

How can I await a callback?

If the callback only calls back once, and the API you are calling does not provide a Promise already (most of them do!) this is the only reason to use a Promise constructor:

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

If await stops execution, does calling an async function return the result directly?

No. If you call an async function, a Promise gets always returned. You can then await that Promise too inside an async function. You cannot wait for the result inside of a synchronous function (you would have to call .then and attach a callback).

Conceptually, synchronous functions always run to completion in one job, while async functions run synchronously till they reach an await, then they continue in another job.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文