Trevor Burnham on promises — and — The spread of my mistake about resolved and fulfilled

177 views
Skip to first unread message

Kris Kowal

unread,
Feb 23, 2012, 2:02:12 PM2/23/12
to Q Continuum
This guy

https://twitter.com/#!/trevorburnham

Wrote a blog on promises in jQuery

http://net.tutsplus.com/tutorials/javascript-ajax/wrangle-async-tasks-with-jquery-promises/

And has a Kickstarter project to fund a book on async programming

http://www.kickstarter.com/projects/869786663/async-javascript-book

It seems that my early mistake of confusing "resolved" and "fulfilled"
has become widespread. It is enshrined in the jQuery promise
implementation now. Thankfully, jQuery’s isResolved is deprecated.

http://api.jquery.com/deferred.state/
http://api.jquery.com/category/deferred-object/

For the record:

A deferred has two states:

- "pending"
- "resolved"

The resolution is (or becomes) another promise internally! All of the
"then" calls (and all other messages) that were called on this
deferred promise get forwarded to the next promise.

There are three kinds of promises:

- "fulfilled"
- "rejected"
- "deferred"

If you call deferred.resolve with a non-promise value, it gets
converted to a fulfilled promise internally. In that case, "resolve"
means "fulfill".

Here’s an over-simplified implementation of each type of promise:

function fulfilled(value) {
return {
then: function (callback, errback) {
var next = Q.defer();
next.resolve(callback(value));
return next.promise;
}
}
}

function rejected(error) {
return {
then: function (callback, errback) {
var next = Q.defer();
next.resolve(errback(error));
return next.promise;
}
}
}

function defer() {
var pending = [];
var next;
return {
promise: {
then: function (callback, errback) {
if (pending) {
pending.push([callback, errback]);
} else {
next.then(callback, errback);
}
}
},
resolve: function (value) {
if (value != null && value.then != null) {
next = value;
} else {
next = fulfilled(value);
}
pending.forEach(function (args) {
nextTick(function () {
next.then.apply(next, args);
});
})
pending = null;
}
}
}

Kris Kowal

John J Barton

unread,
Feb 23, 2012, 2:13:36 PM2/23/12
to q-con...@googlegroups.com
On Thu, Feb 23, 2012 at 11:02 AM, Kris Kowal <kris....@cixar.com> wrote:
> A deferred has two states:
>
> -    "pending"
> -    "resolved"
>
> The resolution is (or becomes) another promise internally! All of the
> "then" calls (and all other messages) that were called on this
> deferred promise get forwarded to the next promise.
>
> There are three kinds of promises:
>
> -    "fulfilled"
> -    "rejected"
> -    "deferred"
>
> If you call deferred.resolve with a non-promise value, it gets
> converted to a fulfilled promise internally. In that case, "resolve"
> means "fulfill".

This is confusing to me. I read "three kinds" as "three types". Thus
"it gets converted to a fulfilled promise internally" makes no sense.

So are there "three types of promises" or did you mean "A promise has
three possible states"?

jjb

Kris Kowal

unread,
Feb 23, 2012, 2:22:31 PM2/23/12
to q-con...@googlegroups.com
On Thu, Feb 23, 2012 at 11:13 AM, John J Barton
<johnj...@johnjbarton.com> wrote:
> So are there "three types of promises" or did you mean "A promise has
> three possible states"?

I definitely wanted to avoid using the term "type". There is certainly
only one promise "interface". There are three implementations of that
interface in Q, but that is an implementation detail. The three
"kinds" of promises could certainly be implemented with a single
"type" that has three states. That is not really important though.

The point is that *conceptually*, a deferred promise can only be
"pending", "fulfilled", or "rejected". If you "resolve" a promise, it
might still be in any of those states, and it is certainly not a
synonym with "fulfilled", which is what the jQuery implementation
implies. For example, the deferred could still be "pending" if you
resolve it with another deferred promise (to do more work). This
happens implicitly every time you chain "then" calls.

Kris Kowal

khs4473

unread,
Feb 23, 2012, 2:32:16 PM2/23/12
to q-con...@googlegroups.com
The jQuery then() goof (if I may call it that) seems to illustrate a widespread misunderstanding about promises.  Namely, that they are synonymous with "callbacks".

Over in Dart, they have a "Future" API that's similar to the jQuery API.

Having a then() that doesn't pipeline baffles me.  What's the point?

khs

Domenic Denicola

unread,
Feb 23, 2012, 2:37:06 PM2/23/12
to q-con...@googlegroups.com
> The jQuery then() goof (if I may call it that) seems to illustrate a widespread misunderstanding about promises. Namely, that they are synonymous with "callbacks".

> Having a then() that doesn't pipeline baffles me. What's the point?

There's some hope, at least:

http://bugs.jquery.com/ticket/11010

Domenic Denicola

unread,
Feb 23, 2012, 2:53:49 PM2/23/12
to q-con...@googlegroups.com
> A deferred has two states:
>
> - "pending"
> - "resolved"
>
> The resolution is (or becomes) another promise internally! All of the
> "then" calls (and all other messages) that were called on this
> deferred promise get forwarded to the next promise.
>
> There are three kinds of promises:
>
> - "fulfilled"
> - "rejected"
> - "deferred"
>
> If you call deferred.resolve with a non-promise value, it gets
> converted to a fulfilled promise internally. In that case, "resolve"
> means "fulfill".

This point is subtle, and comes from the non-obvious fact that you can resolve deferreds with promises. Intuitively I have in the back of my head a picture much like the incorrect one in jQuery: both deferreds and promises have exactly three states, pending/resolved|fulfilled/rejected|broken. And manipulating `deferred` directly manipulates `deferred.promise`.

It might be worth coming up with a newbie-friendly way of explaining this, for the wiki or perhaps the tutorial. I think a key ingredient is explaining exactly what the deferred methods do, and what happens if you call them with promises vs. with non-promise values.

I almost feel that everything would become clearer if deferreds got a `fulfill` method that *didn't* accept promises. (Or rather, if it got a promise, it passed it through without waiting for it to become fulfilled.) Then the majority of my code would switch to using `fulfill` for great clarity, and in the rare cases I want to resolve the deferred with a promise I would use `resolve`.

Hmm :-/.

Wood, Joe

unread,
Feb 23, 2012, 3:13:05 PM2/23/12
to q-con...@googlegroups.com
I just ran into this and had to use Q to wrap the JQuery promise.
Unfortunately it didn't work on ie8 as a <script> import, so I gave up.

Joe

http://bugs.jquery.com/ticket/11010


===============================================================================
Please access the attached hyperlink for an important electronic communications disclaimer:
http://www.credit-suisse.com/legal/en/disclaimer_email_ib.html
===============================================================================

Kris Kowal

unread,
Feb 23, 2012, 4:58:34 PM2/23/12
to q-con...@googlegroups.com
On Thu, Feb 23, 2012 at 12:13 PM, Wood, Joe <joe....@credit-suisse.com> wrote:
> I just ran into this and had to use Q to wrap the JQuery promise.
> Unfortunately it didn't work on ie8 as a <script> import, so I gave up.

I’ll look for some help on this. Thanks for blowing the whistle.

Kris Kowal

Wood, Joe

unread,
Feb 23, 2012, 5:05:35 PM2/23/12
to q-con...@googlegroups.com
I will debug it some more later and send any info I have.
It looked like the Q global was only partially constructed, just didn't
have 'when' and other members. I guess there was something in ie8 that
tripped it up.

Joe

-----Original Message-----
From: q-con...@googlegroups.com [mailto:q-con...@googlegroups.com]
On Behalf Of Kris Kowal
Sent: Thursday, February 23, 2012 4:59 PM
To: q-con...@googlegroups.com
Subject: Re: [Q] Trevor Burnham on promises - and - The spread of my
mistake about resolved and fulfilled

Kris Kowal


Domenic Denicola

unread,
Feb 23, 2012, 5:08:21 PM2/23/12
to <q-continuum@googlegroups.com>, q-con...@googlegroups.com
I run Windows, so can help (although only with IE >= 9).

Is this the kind of thing testling is supposed to solve?

("testling" almost got autocorrected to "testing". That would have been an embarrassing email.)

Wood, Joe

unread,
Feb 23, 2012, 11:06:59 PM2/23/12
to q-con...@googlegroups.com
Isn't there a CommonJS promise specification test?

To solve the chaining issue in JQuery I had to use the 'pipe' method,
which seems to bridge the returned promise so that they can be chained.
Incidentally, JQuery doesn't have Q's 'all' method, instead it uses when
with an argument list. I had to invoke when using 'apply' to convert an
array to an argument list.

Joe

-----Original Message-----
From: q-con...@googlegroups.com [mailto:q-con...@googlegroups.com]
On Behalf Of Domenic Denicola
Sent: Thursday, February 23, 2012 5:08 PM
To: <q-con...@googlegroups.com>
Cc: q-con...@googlegroups.com
Subject: Re: [Q] Trevor Burnham on promises - and - The spread of my
mistake about resolved and fulfilled

===============================================================================

Trevor Burnham

unread,
Feb 26, 2012, 5:56:44 PM2/26/12
to q-con...@googlegroups.com
I'm the aforementioned guy. After an exchange with Kris on Twitter, I decided to file a bug report with jQuery to suggest that they adopt the term fulfilled instead of resolved, starting in jQuery 1.8. If you agree, go comment on the ticket.

Not only would this promote interoperability between jQuery Promises and those that hew to the Promises/A spec, it's also a much better terminology. While writing about jQuery Promises for my upcoming book, Async JavaScript, I've had to use the phrase "resolved or rejected" a lot more often than I'd like. As in, "Additional calls to resolve() or reject() are ignored after the Promise has been resolved or rejected." Sentences like that are both awkward and slightly ambiguous. As I say in the ticket, there really ought to be two dichotomies to describe a Promise's state: fulfilled vs. rejected, and pending vs. resolved.

Trevor Burnham

Mark S. Miller

unread,
Feb 26, 2012, 9:24:46 PM2/26/12
to q-con...@googlegroups.com
Hi Trevor, I tried adding the following comment but could not get past the captcha check. If you know who to forward this to, please do. Thanks.



Note the naturalness wrt real world promise terminology: a fulfilled promise is one that has produced what it promised to produce. A broken promise is one that never will. I believe I coined (or co-coined with Dean Tribble) the use of these terms in the context of non-blocking promises. First at Xanadu http://web.archive.org/web/20071023111712/http://www.sunless-sea.net/Transcripts/promise.html [1] and then again in E. (The Xanadu promises were not non-blocking in the sense we now use the term.) As used in E, the dichotomies were "unresolved" vs "resolved". And of resolved promises, "fulfilled" vs "broken".

Tyler's ref_send library renamed "broken" to "rejected" which seems fine. I'm not sure I've heard "pending" before, but that seems fine as well. However, shifting the meaning of "resolved" so that it means "fulfilled" seems to me to be rather horrible. Yes, please fix this.


[1] "MM A promise can be in one of three states. A promise is born not-ready, then when the result comes back the promise either makes a transition to being fulfilled or broken. So if the request was successful then you have a fulfilled promise, if the request was not successful then the promise turns into a broken promise."

--
    Cheers,
    --MarkM
Reply all
Reply to author
Forward
0 new messages