Re: Promises: Auto-assimilating thenables returned by .then() callbacks: yay/nay?

On May 3, 2013 8:29 AM, "Tab Atkins Jr." <jackalmage@gmail.com> wrote:
> What I've been *trying* to do this entire time is find out *why*
> native nested promises are harmful.  Every. Single. Person. who's
> tried to tell me that nested promises are harmful so far, has ended up
> realizing that they were talking about assimilation, or non-JS
> promises with weird language semantics, or programmer bugs that would
> benefit from causing an error, rather than having the
> excessive-wrapping bug ironed over.

There are several problems with nested promises.

First off they force people to answer the types of questions that
arise in this thread: How do I get to the value inside the doubly
nested promise? Which functions recursively assimilate and which
don't? How do I ensure that my nested promise doesn't get accidentally
unwrapped? Does that matter?

I.e. permitting nested promises creates a more complex model and with
that you always get more confusion and more questions.

Second, and related, is the fact that trying to pass around a
promise-of-a-promise is very hard to make predictably work. There are
several functions in the current Future API which will either take a
Future or a value, and if it's a Future will unwrap it. If it weren't
for nested promises it would be safe to go through arbitrary number of
such APIs. It's likewise also possible to always call
Future.resolve(value) in order to create a promise representing a
value without having to worry about if that value is a promise or not.

With nested promises you can always call Future.accept(myfuture) to
reliably create a nested promise. However once you pass that future
through some other code, you don't know how many times it going to
pass through APIs that may or may not unwrap layers of promises. For
example, you don't know how many times that value might pass as a
return value from a .then() callback, which would cause it to get
unwrapped.

Trying to fix this such that code that receives a nested promise never
unwraps it "too much" makes things much harder. It forces everyone to
be very careful about always ensuring that whenever they unwrap a
layer they always rewrap. The fact that .then() will unwrap the return
value 0 or 1 layers would likely have to change such that it always
unwraps one layer and never accepts raw values.

I.e. the fact that:

doStuff().then(function(v) {
  return v;
}).then(handler);

Here the pass-through .then() handler actually will modify the value
by unwrapping a nested promise one level. This likely wouldn't be
acceptable if you have to care about nested promises retaining their
nesting level.

Third, there's the question of what a nested promise actually means.
Normally a promise represents "a value after some time". But what does
a nested promise mean? "A value after a longer time" obviously isn't
correct since there are no time constraints associated with a promise.
"A promise after some time" also isn't really meaningful given that
that simply means "A value after some time after some time".

This lack of a real mental meaning is what I think is the source of
the problems above. I.e. the fact that there is no good mental model
of what a nested promise actually means makes it hard to reason about
them and tell what is correct behavior and what isn't.

This actually ties back to what the *real* question *should* be:
What's the use case?

I.e. why should we support nested promises? It's a feature and we
don't add features without use cases. A big reason for this is that we
can't verify that a given solution works well unless we can test it
against use cases. So is it a problem that nested promises might get
arbitrarily unwrapped? Is it a problem that they don't get recursively
unwrapped when Future.resolve is used? Hard to say without knowing
what the use cases are.

/ Jonas

Received on Friday, 3 May 2013 23:18:31 UTC