I know defer separates promises states control and process. Using Q as an example, the promise returned by Q.defer().promise
and Q.Promise
are totally different. Why is it designed in this way? And what's the difference between these two Promises?
-
@Claies absolutely not, if you have read both the questions – jiananshi Feb 24 '15 at 03:17
-
@Claies I did read it, it does exactly the same thing as Defer, somehow, I'd like to call it a `sugar method`. Let's take a deep look at Q, Q.promise also returns methods: `resolve` and `reject` https://github.com/kriskowal/q/blob/v1/q.js#L634-L652 But it's slightly different from ES6 Promise, where it `resolve` performs strange. I'm struggling understanding the design purpose for this – jiananshi Feb 24 '15 at 03:43
-
״.defer" is the old API, "new Promise" is the new API. New libraries support only the latter . The reason is that the constructor version is throw-safe. – Benjamin Gruenbaum Feb 24 '15 at 07:55
-
@Claies it's not a dupe... I have no idea why you׳d think it is. – Benjamin Gruenbaum Feb 24 '15 at 07:58
-
see also [Promise.defer() browser support](http://stackoverflow.com/q/27889687/1048572) – Bergi Feb 24 '15 at 13:08
1 Answers
Well, this is about the promise resolution source. Q and a bunch of other libraries offer two APIs:
- The legacy
defer
API - in which you create a deferred that you can.resolve(value)
and it has a promise you can return. - The promise constructor - which is the modern API in which you create the promise from a completion source.
Roughly doing:
var d = Q.defer();
setTimeout(function(){ d.resolve(); }, 1000);
return d.promise;
Is the same as:
return new Promise(function(resolve, reject){
setTimeout(resolve, 1000);
});
so you might be asking
Why do we need two APIs?
Well, the defer API came first. It's how some other languages deal with it, it's how the papers deal with it and it's how people used it first - however - there is an important difference between the two APIs. The promise constructor is throw safe.
Throw safety
Promises abstract exception handling and are throw safe. If you throw inside a promise chain it will convert that exception into a rejection, quoting the spec:
If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason
Let's assume you're parsing JSON from an XHR request:
function get(){
var d = Q.defer();
if(cached) { // use cached version user edited in localStorage
d.resolve(JSON.parse(cached));
} else { // get from server
myCallbackApi('/foo', function(res){ d.resolve(res); });
}
}
Now, let's look at the promise constructor version:
function get(){
return new Promise(function(resolve, reject){
if(cached) { // use cached version user edited in localStorage
resolve(JSON.parse(cached));
} else { // get from server
myCallbackApi('/foo', resolve);
}
});
}
Now, assume somehow your server sent you invalid JSON (or the user edited it to an invalid state) and you cached it.
In the defer version - it throws synchronously. So you have to generally guard against it. In the bottom version it does not. The top version usage would look like:
try{
return get().catch(function(e){
return handleException(e); // can also just pass as function
});
} catch(e){
handleException(e);
}
In the bottom version - the promise constructor will convert throw
s to rejections so it is enough to do:
return get().then(function(e){
return handleException(e);
});
Preventing a whole class of programmer errors from ever happening.

- 1
- 1

- 270,886
- 87
- 504
- 504
-
1Which means, if we are using defer().promise, we have to handle the error manually or it will pops to the window. While in Promise constructor, you don't have to worry about it, that's the difference on **throw safety** – jiananshi Feb 24 '15 at 15:18
-
1@klamtlne yes. That's what I said :) You can read more about the rationale here: http://github.com/promises-aplus/constructor-spec/issues – Benjamin Gruenbaum Feb 24 '15 at 15:21
-
Also, I found it hard to understand why Q.promise does not exposes method such like `then`. If it aims at process control, it shouldn't exposes `resolve` and `reject`. – jiananshi Feb 24 '15 at 15:21
-
@klamtlne `new Q.Promise(...)` constructs a new promise from a creation source. The created object is a promise which indeed has a `then` method. Also see: https://blog.domenic.me/the-revealing-constructor-pattern/ – Benjamin Gruenbaum Feb 24 '15 at 15:23
-
6In the last snippet, it looks like you are handling an exception in the success callback of `then()`. I'm sure I'm not understanding something, but shouldn't it be this: `return get().then(function(s) { /* success */ }, function(e){ return handleException(e); });` – d512 Oct 08 '16 at 05:06