Open ljharb opened 3 years ago
I am hesitant on this. I think if you illustrate use cases you might convince me.
Regarding an allSettled equivalent for dictionaries, I really don't see it. Because the moment one of those promises rejects, the object is not constructible. I think. That said, we definitely could have a case for shaping the exception (i.e. naming the key or the value of the key that failed in the exception to raise).
For race and/or any, I also don't see it. Those to me indicate "either this property is defined, or that property is defined", and it doesn't make sense to me why we'd want it. (But then again, I missed the value of the Map case, so...)
I hesitate also because the more combinations we imagine, the more we bloat the specification. There was the debate over deep versus shallow already, and we have other debates coming (symbols? non-enumerable?).
At some point we might have to think about static functions wrapping around other promise functions to alter their behavior, particularly if we introduce combinatorics to handle all sorts of situations, such as deep promise walking. But I can't think of any ECMAScript functions to do such a modification of how other ECMAScript functions work, and that's a strange minefield to go into. (Edit: The best model I can imagine in this scenario is functions like Array.prototype.map, which take a visitor argument.)
I get that this proposal just appeared, and now is the time to brainstorm. Don't take this as shutting down the conversation. Take this as a note of "I don't know..." and show me the reasoning.
Because the moment one of those promises rejects, the object is not constructible. I think.
Sure it is - the values of the constructed object would be the same settlement objects that Promise.allSettled returns.
There was the debate over deep versus shallow already, and we have other debates coming (symbols? non-enumerable?).
I don't see any use case whatsoever for dealing with anything other than enumerable own properties (string and symbol).
I'm going to put some specific cases regarding rejected promises in #5 as references.
OK, it's clear to me now that I misunderstood about allSettled. I did not realize that it resolves unconditionally when all the promises passed in are no longer pending, and that what it resolves to is an array of simple objects detailing the results. This changes things.
Given that, I can buy an analog for that, and even rewrite the polyfills to use it. But what would you call the dictionary equivalent of allSettled?
I'm not really sure. If we're only providing a single method, then we could perhaps just take "settled" as implied.
My suggestion for a less concise but still useful helper was Promise.allEntries
, which simply unwrapped and resolved an entries like collection. The all
prefix implies the same behavior as Promise.all
, since I'm not sure the other ones make sense. It's not particularly difficult to write in userland, so it may not pass the bar for a useful addition to the language.
Promise.allEntries = (entries) =>
Promise.all(Array.from(entries).map((entry) => Promise.all(entry)));
const obj = {
foo: Promise.resolve("bar"),
};
const resolvedObject = Object.fromEntries(
await Promise.allEntries(Object.entries(obj))
);
const map = new Map();
map.set(Promise.resolve("foo"), Promise.resolve("bar"));
const resolvedMap = new Map(await Promise.allEntries(map.entries()));
Why wouldn’t the other ones make sense? All four have use cases for anything async, whether an iterable of promises or an object of promises.
Because they wouldn't be usable directly with parts of the language that expect an entries collection?
What should allSettledEntries
do, especially in the case of a promise in place of the key (entry[0]
)? The settled result pair isn't usable with Object.entries
, and not very useful with the Map
constructor.
What should anyEntries
(anyEntry
?) do? Return a collection with a single entry where both the key and value first resolved?
Sure it’s usable with Object.entries - the value would be a settlement object, destructurable like ([key, { value, reason, type }]) =>
So only the value part of the entry tuple would be a settlement, the key part would still behave as all
? Aka it'd be more like allEntriesWithValuesSettlement
const settledObject = Object.fromEntries(
await Promise.allSettledEntries([[Promise.resolve("foo"), Promise.resolve("bar")]])
); // {foo: {status: "fulfilled", value: 'bar'}}
Yes, keys can’t be async for a number of reasons, only values would be.
In my suggestion for Promise.allEntries
, keys can definitely be async as
shown above.
That seems like it would be very problematic since two keys could resolve to the same value, so ordering would be important, but you can’t necessarily know that in advance.
I'm not sure I understand how it's different than entries collections today, except that the content of the keys might not be synchronously available. Anyway, probably not worth debating too long since it's the kind of helper that are probably seldom used and can easily be written by users.
Since this proposal appears to be revived, I'd like to raise a suggestion. Perhaps create a namespace under Promise: Promise.object or Promise.dictionary or some other name, where we could define a set of these methods.
For a list of Promises, there's four combinators available.
I personally have use cases for both all and allSettled semantics; would we want all four for completeness?