Skip to content

Array.fromAsync #962

@bnoordhuis

Description

@bnoordhuis

This turns out to be the most annoying new API to implement in quite a while. There are numerous polyfills out there but they're all broken in subtle or often not so subtle ways.

After much trial and error, I put together a polyfill that passes all test262 tests except for one (Array.fromAsync.prototype should not exist but you can't fix that from JS) and believe me when I say you cannot change or remove a single line without breaking some tests.

;(function() {
    "use strict" // result.length=i should throw if .length is not writable
    const {Array, Object, Symbol} = globalThis
    const {asyncIterator, iterator} = Symbol
    Object.defineProperty(Array, "fromAsync", {value:fromAsync, configurable:true, writable:true})
    async function fromAsync(arrayLike, mapFn=undefined, thisArg=undefined) {
        if (mapFn !== undefined && typeof mapFn !== "function") throw new TypeError("not a function")
        let result, i = 0, isConstructor = typeof this === "function"
        let sync = false, method = arrayLike[asyncIterator]
        if (method == null) sync = true, method = arrayLike[iterator]
        if (method == null) {
            let {length} = arrayLike
            length = +length || 0
            result = isConstructor ? new this(length) : Array(length)
            while (i < length) {
                let value = arrayLike[i]
                if (sync) value = await value
                if (mapFn) value = await mapFn.call(thisArg, value, i)
                Object.defineProperty(result, i++, {value, configurable: true, writable: true})
            }
        } else {
            const iter = method.call(arrayLike)
            result = isConstructor ? new this() : Array()
            try {
                for (;;) {
                    let {value, done} = await iter.next()
                    if (done) break
                    if (sync) value = await value
                    if (mapFn) value = await mapFn.call(thisArg, value, i)
                    Object.defineProperty(result, i++, {value, configurable: true, writable: true})
                }
            } finally {
                if (iter.return) iter.return()
            }
        }
        result.length = i
        return result
    }
})()

Now to translate that to C. All those await expressions are going to be a massive pain.

It might actually be an interesting experiment to write complex built-ins in JS and compile to bytecode ahead of time, a la qjsc.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions