Promises

Mark S. Miller erights at google.com
Fri Nov 9 08:33:13 PST 2012


Hi David, thanks for your thoughtful post. I've always used the two-arg
form of .then[1], but your post makes a strong case for more often using
separate one-arg .then and .fail calls. I say only "more often" because the
two arg form can easily make distinctions that the one-arg forms cannot

    var p2 = Q(p1).then(val => { throw foo(val); },
                        reason => { return bar(reason); });

is different than either of the one-arg chainings. It'll be interesting to
look over old code and see how often this difference matters. My guess is
it usually doesn't, in which case your style should dominate.

[1] In my code and in my Q specs <
http://wiki.ecmascript.org/doku.php?id=strawman:concurrency> and
implementations <
http://code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/ses/makeQ.js>,
I have been using "when" rather than "then". Because these are otherwise
compatible AFAICT with A+, for the sake of consensus I'm willing to change
this to "then" everywhere. But before I do, I'd like to make one last plea
for "when" and see how this community responds.

The word "when" is clearly temporal, and suggests postponing something
until some enabling condition. This seems perfect. The word "then" in
programming is most closely associated with the concept of an "if then
else", even though curly bracket languages never spell out the "then". When
I look at your .then/.fail examples, the first thought that always pops
into my head is "Shouldn't the opposite of .then be .else ?" Of course
.fail is appropriate and .else is not. But isn't .then inappropriate for
the same reason?

.then gets especially confusing when dealing with a promise for a boolean.
Consider the asyncAnd operation:

function asyncAnd(answerPs) {
  let countDown = answerPs.length;
  if (countDown === 0) { return Q(true); }
  let {resultP, reject, resolve} = Q.defer();
  for (let answerP of answerPs) {
    Q(answerP).then(answer => {
      if (answer) {
        if (--countDown <= 0) { resolve(true); }
      } else {
        resolve(false);
      }
    }).fail(reason => { reject(reason); });
  }
  return resultP;
}


The code above, for good reason, uses both "then" and "else" in talking
about the same abstract value. This suggests the boolean reading for the
"then" that is just completely wrong. Replacing the "then" with a "when"
makes this code read well IMO.

If this argument fails to persuade us on es-discuss to switch to "when", I
will proceed to replace all my uses of "when" with "then" and declare this
terminology issue over.

Btw, above I tried using your one-arg .then and .fail style. I think this
worked well.


On Fri, Nov 9, 2012 at 4:33 AM, David Bruant <bruant.d at gmail.com> wrote:

> [...]
> ## Q.all
> My favorite feature is the Q.all function. Q.all accepts an array of
> promises and returns a promise which will be fulfilled when all promises
> are. The resolution values are the different promises resolution values:
>
>     var someData1P = query(q1);
>     var someData2P = query(q2);
>     var someData3P = fetch(url); // HTTP GET returning a promise
>
>     Q.all([someData1P, someData2P, someData3P])
>         .then(function(someData1, someData2, someData3){
>             // do something when all data are back
>         })
>
> I used this extensively and it's been extremely helpful. Personally, to
> synchronize different async operations, I've never read code more elegant
> than what Q.all offers. I'm interested in hearing what other's experience
> is on that point.
> Arguably, Q.all could take several arguments instead of accepting only an
> array (that's one thing I'd change). Maybe there is a good reason to
> enforce an array, but I don't know it.
>

I originally speced it to take varargs, but I was thinking in ES6 terms
where "..." is already supported. Kris Kowal points out that most usage of
Q today, for obvious reasons, is pre-ES6. The var-args form means that a
Q.all on a computed list would require usage of .apply, which is much
uglier than the extra square brackets for the non-computed case.

# Promises and progress
>
> Since I started talking about promises, I've had discussions with people
> and one thing that came about a couple of times was the idea of "progress"
> or how promises relate to streams.
>
> The way I see promises, they have 3 states: unfulfilled/resolved/broken.


Sigh. The fact that even you get confused on the terminology makes me think
that no state should ever be named "un" anything. Let's stick with the A+
terminology: pending/fulfilled/rejected.

History aside, I prefer "broken" to "rejected". (E used "broken") But the
verb form of "broken" is "break" which conflicts with a keyword. For this
reason, Tyler chose the verb "reject" for his original Q library, and this
choice stuck. This suggests that we name the state "rejected".


-- 
    Cheers,
    --MarkM
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20121109/408eb8b4/attachment.html>


More information about the es-discuss mailing list