ecma262 icon indicating copy to clipboard operation
ecma262 copied to clipboard

Disallow primitives in `GetIterator()` for async iterables

Open petamoriken opened this issue 1 year ago • 7 comments

Normative conversion have been approved for iterables that will no longer be converted from primitives (https://github.com/tc39/how-we-work/pull/152). There is currently no specification for async iterable to be converted from primitives, and the Web IDL converter is currently working on allowing only objects (https://github.com/whatwg/webidl/pull/1397).

I think it would be better to disallow primitives in GetIterator() for async iterables, since it will probably not cause break the web.

petamoriken avatar Dec 05 '24 13:12 petamoriken

From what I can tell, GetIterator is used to get async iterators for for-await-of loops and yield* inside async generators. I'd be surprised if nobody was using either of those with strings. Is there a good reason to try to do this?

The normative conventions are meant to apply as guiding principles for new features going forward. We did not expect them to be able to be applied retroactively to existing features like this.

michaelficarra avatar Dec 05 '24 22:12 michaelficarra

indeed, (async () => { for await (const x of 'abc') { console.log(x); } })() already works, so i'm not sure how it would be web compatible to change it.

ljharb avatar Dec 05 '24 22:12 ljharb

If only 1.a. of GetIterator is changed, it seems to be possible to change it without any problem for strings. However, I am not sure if that change is effective.

  1. If kind is async, then a. Let method be ? GetMethod(obj, %Symbol.asyncIterator%). b. If method is undefined, then ...

On the topic of the normative conventions, it might be better to reject primitives in Stage 3 Array.fromAsync. cc: @js-choi

petamoriken avatar Dec 06 '24 13:12 petamoriken

However, I am not sure if that change is effective.

Effective at what? What are you trying to achieve? Consistency for how primitives are handled in async iterator taking positions?

michaelficarra avatar Dec 06 '24 15:12 michaelficarra

Main motivation is that we can prohibit prototype pollution for primitives such as Number.prototype[Symbol.asyncIterator]. It is nonsense to make primitives async iterable.

petamoriken avatar Dec 06 '24 17:12 petamoriken

The proposal here from @petamoriken seems to ask that new built-in APIs that accept async iterable inputs (like Array.fromAsync) reject string inputs.

Regarding Array.fromAsync, I am sympathetic to the dislike of strings as direct iterables or async iterables. But I think that Array.fromAsync unfortunately should continue to accept string inputs as iterables. Consistency between Array.fromAsync and for await of, Array.from, and Iterator.from and AsyncIterator.from is unfortunately more important.

See tc39/proposal-iterator-helpers#250: Iterable.from and AsyncIterable.from were going to prohibit string inputs, but Committee consensus reversed this decision because of the strong relationship between them, for of, and for await of.

Array.fromAsync would likely be the last API that follows the strings-are-directly-iterable mistake. Unless we ever decide to make fromAsync for TypedArrays.

js-choi avatar Apr 30 '25 03:04 js-choi

But I think that Array.fromAsync unfortunately should continue to accept string inputs as iterables. Consistency between Array.fromAsync and for await, Array.from, and Iterator.from and AsyncIterator.from is unfortunately more important.

Make sense.

To give some background, there was a discussion on the WHATWG side that ReadableStream.from should reject primitives, and I was suggested that Array.fromAsync should follow suit, but it ended up accepting string | async iterable<T>. See https://github.com/whatwg/webidl/pull/1397#issuecomment-2612481040.

petamoriken avatar Apr 30 '25 04:04 petamoriken