How about awaiting Arrays ?
On Fri, Mar 3, 2017 at 12:43 PM, Andrea Giammarchi < andrea.giammarchi at gmail.com> wrote:
Not the first time I accidentally type the following:
const allTheThings = await [pa, pb, pc];
I am assuming that JS will implicitly realize that'd be a
Promise.all([pa, pb, pc])
call but nope.
The problem is that an array of promises is a perfectly valid promise
resolution value. (And that the proposal is now at Stage 4. :-) ) To
do this, await
would have to treat arrays specially in a way that
promises don't. That seems like something await
shouldn't do. You
might propose awaitall
, awaitany
, awaitrace
or similar...
Then I also realize it'd be cool to have other shortcuts too that play nice with arrays such:
Array.prototype.all = function all() { return Promise.all(this); }; Array.prototype.race = function race() { return Promise.race(this); };
Side note: I'm fairly sure all
isn't websafe as an Array.prototype
function (isn't that why ES5 used every
?).
I don't think arrays need special promise-related functionality in the
standard lib. But if they got it, I'd want it to be reflected in the
naming, e.g. [].promiseAll
, [].promiseAny
, ... Both for clarity and
for web safety (I'm fairly sure all
on Array.prototype
wouldn't be
web-safe).
-- T.J.
If this is what we gonna have
[].promiseAll
then I'd rather
Promise.all()
You made some fair point, I guess there's nothing to see here.
I think this conversation needs to happen but I am not sure baking it into Array facilities makes the most sense.
In my experience with async/await I am very often needing Promise.all but
in some cases the other forms of multi promise capabilities. What if we
expanded the keyword await.all [...]; await.race [...]
or add a new
context for of
outside for loops but this would be limited to P.all
behavior await of [...]
?
- Matthew Robb
On Fri, Mar 3, 2017 at 3:28 PM, Matthew Robb <matthewwrobb at gmail.com> wrote:
I think this conversation needs to happen but I am not sure baking it into Array facilities makes the most sense.
In my experience with async/await I am very often needing Promise.all but in some cases the other forms of multi promise capabilities. What if we expanded the keyword
await.all [...]; await.race [...]
Heh, I was thinking of that too, as an alternative to the awaitall
and such I suggested to Andrea so we didn't need to add Yet More
Keywords. :-)
await.all iterable;
instead of
await Promise.all(iterable);
? That seems pretty clear to me, and doesn't seem like a big specification or implementation burden (he said, talking through his hat).
-- T.J.
Actually I would go with
await ...expr;
As sugar for:
await Promise.all(Array.from(expr))
On 3 Mar 2017 17:15, "T.J. Crowder" <tj.crowder at farsightsoftware.com> wrote:
On Fri, Mar 3, 2017 at 3:28 PM, Matthew Robb <matthewwrobb at gmail.com> wrote:
I think this conversation needs to happen but I am not sure baking it into Array facilities makes the most sense.
In my experience with async/await I am very often needing Promise.all but in some cases the other forms of multi promise capabilities. What if we expanded the keyword
await.all [...]; await.race [...]
Heh, I was thinking of that too, as an alternative to the awaitall
and such I suggested to Andrea so we didn't need to add Yet More
Keywords. :-)
await.all iterable;
instead of
await Promise.all(iterable);
? That seems pretty clear to me, and doesn't seem like a big specification or implementation burden (he said, talking through his hat).
-- T.J.
On Fri, Mar 3, 2017 at 4:51 PM, Michał Wadas <michalwadas at gmail.com> wrote:
Actually I would go with
await ...expr;
As sugar for:
await Promise.all(Array.from(expr))
Which is great for Promise.all
, but leaves us without race
or things
that may be added in future (like Andrea's any
). (Granted all
has to be
the dominant use case...)
Why the Array.from
part of that?
-- T.J.
On Fri, 3 Mar 2017 at 16:51 Michał Wadas <michalwadas at gmail.com> wrote:
Actually I would go with
await ...expr;
I think await.all
is clearer and more explicit, and await.race could be
added too.
Also: here's previous discussion that didn't go anywhere: esdiscuss.org/topic/proposal-await-p1-p2-equivalent-to-await-promise-all-p1-p2 .
My mistake Array.from is not necessary because Promise.all accepts any iterable.
Though I don't believe .race to be common use case (especially when we consider it's edge case with empty array).
Hsving parity with spread and rest parameters seems consistent for me.
On Fri, Mar 3, 2017 at 8:51 AM, Michał Wadas <michalwadas at gmail.com> wrote:
Actually I would go with
await ...expr;
I have not liked any of the prior suggestions on this thread. But this one is interesting. It has no compatibility problems, it composes nicely, and it suggests its meaning clearly.
On Fri, Mar 3, 2017 at 12:27 PM, Mark S. Miller <erights at google.com> wrote:
On Fri, Mar 3, 2017 at 8:51 AM, Michał Wadas <michalwadas at gmail.com> wrote:
Actually I would go with
await ...expr;
I have not liked any of the prior suggestions on this thread. But this one is interesting. It has no compatibility problems, it composes nicely, and it suggests its meaning clearly.
I should have read ahead. await.all and await.race also have these virtues, and there's no generalization of await ...expr that naturally expresses race.
First, I'll start out with this: I tend to be very adverse to new syntax,
but I'll draw exceptions for things that enable whole new ways of looking
at and manipulating code, like async functions and decorators, or things
that enable new functionality altogether, like function.sent
or private
class fields. Things that haven't hit that bar for me include the bind
syntax proposal (beyond function pipelining) and the
await.all
/await.race
idea here.
BTW, I had some ideas on unifying that with observables at the syntax level
here, particularly with my parallel
and await parallel
ideas there:
tc39/proposal-observable#141
Basically, it unifies the common case of merging promises and observables with a fairly low syntactic footprint.
As for Promise.race
, I see it much less frequently, and it's much simpler
and faster under the hood to implement due to less state needed, so I
didn't see the need to add support for that.
Isiah Meadows me at isiahmeadows.com
Honestly Isiah my largest motivation for thinking this is worth solving boils down to the fact that async/await does a good job of hiding the promise and generator based implementation under it but this falls down so fast when adding Promise.all.
I'm helping a new person learn JavaScript right now and he's using fetch to get some JSON. Explaining the first then returning res.json() and the second one chaining as well as 'this' considerations was a disaster. He conceptually understands asynchronous code so when I backed up and did the same thing with async/await he just got it. Saving him from needing to learn anything about promises until later on.
In my opinion if a layer of sugar doesn't fully abstract the layers it sits upon then it's an incomplete and confusing feature. I'd go so far as to say that async/await should always support every capability of promise without anyone touching Promise directly.
glad this topic moved some interest, but I'd like to share my opinion about
locking down to Promise.all
only any possible solution, as example:
I don't believe .race to be common use case (especially when we consider
it's edge case with empty array).
As mentioned already, fetch
API is a clear case where you want to fetch
races, if not actually fetching any in case the resource wasn't present in
the local cache.
Fetch API has this funny little gotcha that erase somehow unpredictably and Promise.any would solve many things there, granting that at least, instead of an error, the user donwloaded the last online version of that resource and tried to store it again.
Race on NodeJS side plays also very well when you have an pool of n odes pointing at different DB instances, all synchronized and happy, all capable of falling back to other nodes.
Race in these cases is a way more common use case.
Promise.all is actually useful for list of tasks to wait for, like loading modules asynchronously, but for all cases where the full list ain't needed, it makes the program slow for no reason.
I know you all know these things but then again, I use race and any quite often and reading "not a common use case" didn't match reality here.
Last, but not least, await.all
and friends looks even better proposal
than [].all
to me, specially because it's not related to Arrays and it
can work with any iterable.
It's also future friendly for .race
and .any
too :party-emoji:
Best
I'm going to toy with my strawman some, previously introduced here: tc39/proposal-observable#141
In particular, I'm going to try to address some of these concerns in an updated proposal. My goal is to come up with something that unifies both Promises and Observables, and can model all these forms of data flow declaratively (except second) and elegantly:
- Await all success, await first failure:
Promise.all
- Await all success, await all failure: N/A (not sure of use case)
- Await first success, await first failure:
Promise.race
- Await first success, await all failure:
Promise.any
I know it'll be difficult, but I'd like to come up with something that will be fast, usable, and highly intuitive.
Isiah Meadows me at isiahmeadows.com
As someone who teaches JavaScript a lot, I believe that not explaining what promises are when teaching async/await is not possible, because await only works on promises. You need to at least understand that promises are "async values" to understand and use aysnc/await. What is abstracted away is the use of chainable promises and .then/.catch, and not the promises themselves.
So saying that Promise.all accepts an array of promises and returns a promise that is resolved only when they all resolve is not leaking the async/await abstraction. It's still "OK".
There are hundreds of little syntax sugars we can add to the language. We should choose carefully because each one makes the language more complicated. Abstracting away promise chaining with async/await is a game changer, and is worth the "syntactic sugar" - even more so IMHO than class abstracted away prototype (which is a much leakier "syntax sugar"). Abstracting away Promise.all with syntactic sugar gives us little gain, and in my opinion is not worth it.
I like the following way of using Promise.all()
:
const all = Promise.all.bind(Promise);
const allTheThings = await all([pa, pb, pc]);
Nice one, but at that point const all = a => Promise.all(a)
seems a
better option.
I think the point here is that we all need that and repeating the pattern every single time feels like a very clunky experience.
await.all
looks like a win 🎉
Not the first time I accidentally type the following:
const allTheThings = await [pa, pb, pc];
I am assuming that JS will implicitly realize that'd be a
Promise.all([pa, pb, pc])
call but nope.Then I also realize it'd be cool to have other shortcuts too that play nice with arrays such:
Array.prototype.all = function all() { return Promise.all(this); }; Array.prototype.race = function race() { return Promise.race(this); }; // with the lovely addiction of ... Array.prototype.any = function any() { return Promise.any(this); }; // with Promise.any being (sorry it was a tweet) Promise.any = $ => new Promise((D,E,A,L) => { z = []; _ = $.map(($, i) => Promise.resolve($) .then(D, O => { z[i] = O; --_ || E(z) }) ).length });
So that ...
const allTheThings = await [pa, pb, pc].all();
Yay? Nay? Silly? no-way?
I thought it was worth it to point this out.
Best