4

In Angular, the following snippets seem to be equivalent:

let deferred = $q.defer();
if(whatever) {
   deferred.resolve('something');
}
else {
   deferred.reject('nah');
}
return deferred.promise;

and

return $q((resolve,reject) => {
    if(whatever) {
       resolve('something');
    }
    else {
       reject('nah');
    }
});

My question: if they aren't equivalent, how do they differ? If they are equivalent, is there a compelling reason for preferring one over the other?

csvan
  • 8,782
  • 12
  • 48
  • 91
  • https://docs.angularjs.org/api/ng/service/$q The purpose of the deferred object is to expose the associated Promise instance as well as APIs that can be used for signaling the successful or unsuccessful completion, as well as the status of the task. – Noctisdark Mar 14 '16 at 14:23

3 Answers3

5

The second version that you posted is the one that follows the Promise/A+-Specification.

From a functional perspective, both versions are equivalent. The first version (in my eyes) makes it easier to read the asynchronous code as synchronous one, but the second one uses the syntax that has made it into ES2015 and will also be part of ES6, i.e. the next versions of javascript.

Thus, the choice is up to you, but you can expect to see a lot more of the second type of promise-syntax in the future.

burnedikt
  • 962
  • 5
  • 17
  • No, Promise/A+ does not specify how promises are constructed. Both deferreds and constructors with executors can be used. – Bergi Jun 09 '16 at 13:28
3

As far as $q promises go, there isn't a huge behavioral difference in one vs. the other. The main benefit of the second approach is that it uses the same style as native promises, and it nicely contains the logic for resolving the promise instead of having a loose deferred dangling around (this is probably one of the reasons why native promises went with the latter style).

I had mistakenly said that the second approach will catch synchronous errors in the passed in function and convert them into a rejected promise. This is true for native promises, Q promises, Bluebird promises, and possibly others, but it is not true for $q(). $q will just treat the error as an uncaught error and log it to the console.

Here is an example, using Bluebird promises, of what I was describing. It's a great reason to use the latter style if you are using promises other than $q:

function queryValue(callback) {
  throw new Error("Not implemented yet!!!");
}

function makeConstructorPromise() {
  return new Promise(function(resolve) {
    queryValue(function(value) {
      resolve(value);
    });
  });
}

function makeDeferredPromise() {
  let deferred = Promise.defer();

  queryValue(function(value) {
    deferred.resolve(value);
  });

  return deferred.promise;
}

makeConstructorPromise()
  .catch(function(error) {
    console.error('caught the constructed promise error!', error);
  });

makeDeferredPromise()
  .catch(function(error) {
    // not caught
    console.error('caught the deferred promise error!', error);
  });
<script src="https://cdnjs.cloudflare.com/ajax/libs/bluebird/3.5.1/bluebird.min.js"></script>

The deferred approach is old-fashioned and may have practical applications in very specific cases, but 99% of the time, the constructor approach is the way to go.

It is safer (in most promise implementations) and is consistent with the way ES6 promises work.

JLRishe
  • 99,490
  • 19
  • 131
  • 169
  • strange, the docs say the opposite: https://docs.angularjs.org/api/ng/service/$q - "Note: unlike ES6 behavior, an exception thrown in the constructor function will NOT implicitly reject the promise." – Marnes Nov 29 '17 at 19:04
  • @Marnes Yikes, you're right. I guess I had just assumed that the `$q` function worked like the Promise constructor from ES6, Bluebird, and Q, but it doesn't. I'll correct my answer in a few hours. – JLRishe Nov 29 '17 at 19:20
  • Hey man, don't forget to edit your answer like you said. – Marnes Dec 09 '17 at 19:44
  • @Marnes Updated it. Sorry for the delay. – JLRishe Dec 09 '17 at 20:45
0

The functionality is equivalent - its more about "style".

One syntax is more like Kris Kowals Q, which was one of the first implementations of promises in javascript.

The other syntax is more like the implementation that we will receive with ES6.

So use, what you are more comfortable with - if you begin to learn, i sugggest to use the ES6 way, cause this will be the future of "standard javascript" and not only angular.

More informations about this topic and your question, you could find directly in the Angualar Docu: https://docs.angularjs.org/api/ng/service/$q/