Allow "awaiting" on arrays

What if the array is just an array of objects that happen to have .then methods but don't follow the thenable protocol? Also, there's a valid use case for returning arrays of promises without flattening them - you might want to chain the inner promises separately for whatever reason (think: tasks and such).

Pretty sure this would also be web-incompatible to boot, but I have neither numbers nor access to the tools to verify that claim.


To make this work, you’d have to have Array.prototype.then defined; this would immediately destroy any website that used a promise for an array.


And what if it would be a different operator, lets name it for now dawait for "deep await" that would await not only for emidiate operand to be resolved but for any deep property of resolved operand to be resolved as well? So it would work for arrays of promises, objects of promises etc.?

1 Like

Well, the result should be the same that what happens with the Promise.all() method. This must be just a simplified way to do it.

This implementation does not force you to always flat your arrays of promises. You should be able to just return arrays of promises as always and manipulate them as usual.

The whole idea around this, is to define and standardize the Array.prototype.then

Perhaps a little bit more complex if we want to make "thenable" any kind of iterator.

What I'm questioning with this is the necessity of Promise.all() by itself (for arrays of promises).
If Array.prototype.then is not defined yet, what stop us to define this behavior?

const array = [];
array.then(result => {

This ↑ has a lot more sense to me.

Not sure if I get it. Could you explain this to me?

Well, for one, Promise.all already returns a promise for an array. This means array can never ever be thenable, or every usage of Promise.all would break (it would also mean that “what Would Array.prototype.then produce a Promise for” be unanswerable).

1 Like

I generally like the idea of making it easier to work with an array of Promises, but I don't think this should be implemented for arrays in general.

The main point against Array.prototype.then for me is that returning a regular array, let's say with a few million elements from an async function (or resolving a promise with it), would then cause the whole array to be checked wether one of the elements contains a thenable. That's a major performance for a little benefit.

I'd rather propose something along the lines of:

  class AsyncArray extends Array {
     then(resolve, reject) {
        Promise.all(this).then(resolve, reject);

  // usage:
  await new AsyncArray(1, 2, 3).map(async n => n)


I tried to make my own implementation (or polyfill) for this but I faced this interesting problem...

So it fail. But for a different reason.

Didn't we have await * for exactly this?

I wrote a polyfill for it before. But the solution is quite hacky (and might be buggy in nestted await or finally block).

DONT USE IT!!!! I have a better version on post #16 Allow "awaiting" on arrays

Array.prototype.then = function x(resolve, reject) {
    Array.prototype.then = undefined
    const restore = f => value => {
        const result = typeof f === 'function' ? f(value) : value
        Array.prototype.then = x
        if (f === reject && typeof f === 'function') return Promise.reject(value)
        return result
    return Promise.all(this).then(restore(resolve), restore(reject))

That seems like it would create the possibility that code could run in between the deletion and restoration of Array.prototype.then, causing it to break in unpredictable ways.

I would strongly caution everyone not to speculatively modify builtins, even in channels like this, since people often copy-paste code they find on the internet :-)


A await* expr mentioned by @jridgewell looks good

In another way, it's boring to write

await Promise.all( () { ... }))

If there is a syntax sugar for it, things will be nicer

for parallel await (const x of arr) {

I have a better polyfill now, it won't delete & restore the prototype @ljharb


Array.prototype.then = async function (resolve, reject) {
    class NonThenableArray extends Array {}
    NonThenableArray.prototype.then = 0
    NonThenableArray.prototype[Symbol.species] = NonThenableArray
    const result = new NonThenableArray()
    // This won't make promises parallel
    for (const p of this) {
        try { result.push(await p) }
        catch (e) { return reject(e) }

Seems like you'd want return Promise.allSettled(this) as the entire invocation instead of needing NonThenableArray? Either way tho, it's still dangerous to have examples of code that mutates builtins; all it takes is enough websites using code that does it and we'd never be able to use the name :-)

Yeah. It's just an example, and I won't encourage anyone to use it seriously.

But the ergonomics of handling multiple async tasks is bad today. Does it considerable things like await* array or for await* (const x of expr)?

await * imo doesn't convey anything about what it's doing; if it's using Promise.all semantics, then what about race, allSettled, or any?

I could see an await.all/await.race/await.allSettled/await.any, though (with the implication that any new combinators would get the same support).


await.all / await.race / await.allSettled / await.any

Cool. What about making it a proposal?

(Upper case in the meta property look a bit strange)

1 Like

Please checkout the proposal (full spec text)

1 Like