Simplifying promises in Javascript
Asked Answered
U

1

7

Are the following code snippets equivalent?

Version 1

function doSomething() {
  var defer = $q.defer();

  foo().then(function() {
    bar().then(function() {
      defer.resolve();
    });
  });

  return defer.promise;
}

Version 2

function doSomething() {
  return foo().then(bar);
}
Unify answered 29/7, 2014 at 11:51 Comment(0)
C
6

There are many differences between these two approaches.

The major difference between the two snippets is that in version 2 your are implicitly passing the resolved value from foo directly to bar. In addition to that doSomething will resolve whatever bar will resolve to, whereas in version 1 the result is discarded.

Some additional important points made by Benjamin Gruenbaum:

(a) if bar is a reference error 1 rejects the inner promise and 2 throws.
(b) 1 requires a reference to $q where 2 is implementation agnostic.
(c) version 1 is not exception safe and a rejection will be swallowed where version 2 will let you .catch a rejection. ; There are several smaller differences as well. See stackoverflow.com/questions/22539815

You could also write it like this.
This way you don't get the implicit pass trough of the resolved value from foo to bar ( it's explicit now ), which can be confusing or easily overlooked. It can also be useful if you want to do something with the resolved values of foo or bar before returning them.

function doSomething() {
  return foo().then(function(res){
     // maybe  do something with the result of foo
     return bar(res);
  }).then(function(res){
     // maybe do something with the result of bar
     return res;
  });
}

Manually creating a deferred object should be kept to a minimum and is generally an anti pattern.

https://github.com/petkaantonov/bluebird/wiki/Promise-anti-patterns#the-deferred-anti-pattern

The key principle being demonstrated here is that a promise will adopt the state of the returned promise ( or thenable ) inside it's resolve method.

Chaney answered 29/7, 2014 at 11:57 Comment(5)
I am shocked about the 'manually creating a deffered object is an anti-pattern' statement. I am doing this 10 times daily since I have adopted the technique from various blogs and tutorials. Thanks for pointing outChromosome
@Sprottenwels checkout my answer here for some more info on that: https://mcmap.net/q/1624174/-dont-understand-javascript-promise-antipattern-returning-promise-from-callbackLandside
@Sprottenwels see #23804243 you should only create deferred objects when promisifying an API or writing aggregation methods - rarely the case. For instance I have a 100KLoC code base with Bluebird without a single deferred or promise constructor.Witching
There is no point in the anonymous functions added here, the statement here about anonymous functions wrapping function references is true in every case and not just promises. Some other differences you should probably edit in: (a) if bar is a reference error 1 rejects the inner promise and 2 throws. (b) 1 requires a reference to $q where 2 is implementation agnostic. (c) version 1 is not exception safe and a rejection will be swallowed where version 2 will let you .catch a rejection. ; There are several smaller differences as well. See stackoverflow.com/questions/22539815Witching
While on the point of exception handling - I'd also recommend this #21800510 and #22520284Witching

© 2022 - 2024 — McMap. All rights reserved.