- From: Domenic Denicola <notifications@github.com>
- Date: Mon, 29 Jul 2024 23:34:50 -0700
- To: whatwg/webidl <webidl@noreply.github.com>
- Cc: Subscribed <subscribed@noreply.github.com>
- Message-ID: <whatwg/webidl/pull/1397/review/2206732123@github.com>
@domenic commented on this pull request. Thanks for working on this! I think the theoretically-language-agnostic part is not quite separated nicely from the JS language part, and we can make the latter more rigorous. But the feedback is largely editorial. > @@ -6177,6 +6207,42 @@ sequence is used. Any [=list=] can be implicitly treated as a <code>sequence<|T|></code>, as long as it contains only [=list/items=] that are of type |T|. +<!-- Note: if we ever add synchronous iterable types, we should add a note here about why sequences and iterables are not the same. --> + +<h4 id="idl-async-iterable-type" lt="async iterable" dfn export>Async iterable types — async iterable<|T|></h4> + +The <dfn lt="async iterable type" export>async iterable<|T|></dfn> type is a parameterized +type whose values are references to objects that can produce an [=async iterator=]. This doesn't seem to match how other parameterizable types are defined. I'd expect just "async iterable" as the dfn text. > @@ -6177,6 +6207,42 @@ sequence is used. Any [=list=] can be implicitly treated as a <code>sequence<|T|></code>, as long as it contains only [=list/items=] that are of type |T|. +<!-- Note: if we ever add synchronous iterable types, we should add a note here about why sequences and iterables are not the same. --> + +<h4 id="idl-async-iterable-type" lt="async iterable" dfn export>Async iterable types — async iterable<|T|></h4> + +The <dfn lt="async iterable type" export>async iterable<|T|></dfn> type is a parameterized +type whose values are references to objects that can produce an [=async iterator=]. + +Async iterables are passed by reference in language bindings where they are represented by an object. +This means that passing an async iterable to a [=platform object=] will result in a reference to the +async iterable being kept by that object. Similarly, any async iterable returned from a platform +object will be a reference to the same object and modifications made to it will be visible to the +platform object. This is in contrast to sequences, which are always passed by value. + +Note: Async iterables can not be constructed from IDL. If returned from an operation, or used as the ```suggestion Note: Async iterables cannot be constructed from IDL. If returned from an operation, or used as the ``` > + +<h4 id="idl-async-iterable-type" lt="async iterable" dfn export>Async iterable types — async iterable<|T|></h4> + +The <dfn lt="async iterable type" export>async iterable<|T|></dfn> type is a parameterized +type whose values are references to objects that can produce an [=async iterator=]. + +Async iterables are passed by reference in language bindings where they are represented by an object. +This means that passing an async iterable to a [=platform object=] will result in a reference to the +async iterable being kept by that object. Similarly, any async iterable returned from a platform +object will be a reference to the same object and modifications made to it will be visible to the +platform object. This is in contrast to sequences, which are always passed by value. + +Note: Async iterables can not be constructed from IDL. If returned from an operation, or used as the +type of a dictionary member, the async iterable will have originated from the host environment and +have been turned into an IDL type via a language binding. Instead of returning an async iterable +from an IDL operation, the operation may want to return an [=interface=] that has an ```suggestion from an IDL operation, the operation might want to return an [=interface=] that has an ``` > +[=asynchronously iterable declaration=]. + +Async iterables must not be used as the type of an [=attribute=] or [=constant=]. + +There is no way to represent an async iterable value in IDL. + +<h5 id="idl-async-iterator-object" lt="async-iterator" dfn export>Async iterator objects</h5> + +An <dfn lt="async iterator" export>async iterator<|T|></dfn> value is a reference to an object +that can produce a sequence of values asynchronously. The async iterator value is parameterized by a +type |T|, which defines the types of values produced by this async iterator. + +Async iterators, unlike sequences, do not have a fixed length and can be infinite. Values are +asynchronously produced as the async iterator is iterated over. + +Async iterators are values, not types, and thus cannot be used as a type in IDL. They can only be This seems pretty confusing. What type to the created values have, then? I think this whole section is JS language binding specific, since it relates to algorithms like "open" and "get the next value" which are only defined for the JS language binding. So maybe we can remove this confusing not-a-type from the Web IDL "Types" section (2.13), and make it something produced and consumed by those algorithms? > @@ -8115,6 +8181,161 @@ JavaScript Array values. </div> +<h4 id="js-async-iterable">Async iterable — async iterable<|T|></h4> + +IDL <a lt="async iterable type">async iterable<|T|></a> values are represented by a JavaScript +object, a JavaScript method, and a flag indicating whether the method is expected to produce a +<code>sync</code> or <code>async</code> iterator. Can we give these fields proper `<dfn>`s? We're not consistent about this in general but I'd like to be here since this is one of the more complex fields we have. #idl-callback-interface has an example where it defines `callback context` (although it tries to be language-agnostic, which is probably not right). Something like ```html In the JavaScript binding, IDL [=async iterable type|async iterable=] values are represented by a [=struct=] with the following [=items=]: * <dfn for="JS async iterable">object</dfn>, a JavaScript value * <dfn for="JS async iterable">method</dfn>, a JavaScript value * <dfn for="JS async iterable">type</dfn>, either "<code>sync</code>" or "<code>async</code>" ``` and then reference those struct items throughout. > + 1. Return the IDL async iterable value that represents a reference to the JavaScript object + |V|, the JavaScript method |method|, and the flag <code>async</code>. +</div> + +<div id="async-iterable-to-js" algorithm="convert an async iterable to a JavaScript value"> + An IDL <a lt="async iterable type">async iterable<<var ignore>T</var>></a> value is + [=converted to a JavaScript value|converted=] to a JavaScript object as follows: + + 1. Return the JavaScript object that represents the same async iterable as the IDL value. +</div> + +<h5 id="js-async-iterator">Async iterators</h5> + +IDL [=async iterator=] values are represented by JavaScript [=Iterator=] records. + +Async iterators can only be created from an [=async iterable type=]. Specifically by the "open" algorithm, right? > +</div> + +<div id="async-iterable-to-js" algorithm="convert an async iterable to a JavaScript value"> + An IDL <a lt="async iterable type">async iterable<<var ignore>T</var>></a> value is + [=converted to a JavaScript value|converted=] to a JavaScript object as follows: + + 1. Return the JavaScript object that represents the same async iterable as the IDL value. +</div> + +<h5 id="js-async-iterator">Async iterators</h5> + +IDL [=async iterator=] values are represented by JavaScript [=Iterator=] records. + +Async iterators can only be created from an [=async iterable type=]. + +<h5 id="js-async-iterator-iteration">Iterating async iterator</h5> ```suggestion <h5 id="js-async-iterator-iteration">Iterating async iterators</h5> ``` > + To <dfn id="async-iterable-open" export lt="open an async iterable">open</dfn> an + <code><a lt="async iterable type">async iterable<<var>T</var>></a></code> |iterable|, + perform the following steps: ```suggestion To <dfn id="async-iterable-open" export lt="open an async iterable">open</dfn> an <code><a lt="async iterable type">async iterable<<var>T</var>></a></code> |iterable|: ``` here and below > +IDL [=async iterator=] values are represented by JavaScript [=Iterator=] records. + +Async iterators can only be created from an [=async iterable type=]. + +<h5 id="js-async-iterator-iteration">Iterating async iterator</h5> + +[=Async iterables=] are not directly iterated over. They are first opened, to create a new +[=async iterator=], and then the [=async iterator=] is iterated over. + +<div algorithm> + + To <dfn id="async-iterable-open" export lt="open an async iterable">open</dfn> an + <code><a lt="async iterable type">async iterable<<var>T</var>></a></code> |iterable|, + perform the following steps: + + 1. Let |object| be the |iterable|'s JavaScript object. ```suggestion 1. Let |object| be |iterable|'s JavaScript object. ``` here and below. > + +[=Async iterables=] are not directly iterated over. They are first opened, to create a new +[=async iterator=], and then the [=async iterator=] is iterated over. + +<div algorithm> + + To <dfn id="async-iterable-open" export lt="open an async iterable">open</dfn> an + <code><a lt="async iterable type">async iterable<<var>T</var>></a></code> |iterable|, + perform the following steps: + + 1. Let |object| be the |iterable|'s JavaScript object. + 1. Let |method| be the |iterable|'s JavaScript method. + 1. Let |iterator| be [=?=] <a abstract-op>GetIteratorFromMethod</a>(|object|, |method|). + 1. If |iterable| has a flag that shows that it is expected to produce a + <code>sync</code> iterator, then: + 1. Set |iterator| to <a abstract-op>CreateAsyncFromSyncIterator</a>(|iterator|). Nit: don't nest single-line if statements > + 1. Return the IDL async iterable value that represents a reference to the JavaScript object + |V|, the JavaScript method |syncMethod|, and the flag <code>sync</code>. + 1. Return the IDL async iterable value that represents a reference to the JavaScript object + |V|, the JavaScript method |method|, and the flag <code>async</code>. +</div> + +<div id="async-iterable-to-js" algorithm="convert an async iterable to a JavaScript value"> + An IDL <a lt="async iterable type">async iterable<<var ignore>T</var>></a> value is + [=converted to a JavaScript value|converted=] to a JavaScript object as follows: + + 1. Return the JavaScript object that represents the same async iterable as the IDL value. +</div> + +<h5 id="js-async-iterator">Async iterators</h5> + +IDL [=async iterator=] values are represented by JavaScript [=Iterator=] records. Similarly I think we can formally define this as a struct that contains "type parameter" and "record" fields. > + 1. Let |V| be [=?=] <a abstract-op>IteratorValue</a>(|iterResult|). + 1. Let |value| be the result of [=converted to an IDL value|converting=] |V| to an IDL + value of type |T|. + 1. Return |value|. + +</div> + +<div algorithm> + + To <dfn id="async-iterator-close" export lt="close an async iterator">close</dfn> an + <code><a lt="async iterator">async iterator<<var ignore>T</var>></a></code> |iterator|, + with a reason |reason|, perform the following steps: + + 1. Let |iteratorRecord| be the [=Iterator=] represented by |iterator|. + 1. Let |iteratorObj| be |iteratorRecord|.\[[Iterator]]. + 1. Let |returnMethod| be <a abstract-op>GetMethod</a>(|iteratorObj|, "return"). ```suggestion 1. Let |returnMethod| be <a abstract-op>GetMethod</a>(|iteratorObj|, "<code>return</code>"). ``` > + +</div> + +<div algorithm> + + To <dfn id="async-iterator-close" export lt="close an async iterator">close</dfn> an + <code><a lt="async iterator">async iterator<<var ignore>T</var>></a></code> |iterator|, + with a reason |reason|, perform the following steps: + + 1. Let |iteratorRecord| be the [=Iterator=] represented by |iterator|. + 1. Let |iteratorObj| be |iteratorRecord|.\[[Iterator]]. + 1. Let |returnMethod| be <a abstract-op>GetMethod</a>(|iteratorObj|, "return"). + 1. If |returnMethod| is an abrupt completion, return [=a promise rejected with=] + |returnMethod|.\[[Value]]. + 1. If |returnMethod| is <emu-val>undefined</emu-val>, return [=a promise resolved with=] + <emu-val>undefined</emu-val>. ```suggestion {{undefined}}. ``` (converting from ES-land to IDL-land) > + given |returnPromiseResult|: + 1. If <a abstract-op>Type</a>(|returnPromiseResult|) is not Object, [=JavaScript/throw=] a + <l spec=ecmascript>{{TypeError}}</l>. + 1. Return <emu-val>undefined</emu-val>. + +</div> + +<div class="example" id="example-js-async-iterable"> + + <code>concatN</code> is an [=operation=] that returns a promise that will be fulfilled with the + concatenation of all the strings yielded by the async iterable passed to it. It stops + concatenating and closes the iterator once the async iterable has yielded N strings. + + <pre> + interface I { + Promise<DOMString> concat(async iterable<DOMString> strings, unsigned long maxN); ```suggestion Promise<DOMString> concatN(async iterable<DOMString> strings, unsigned long maxN); ``` > + 1. If |returnResult| is an abrupt completion, return [=a promise rejected with=] + |returnResult|.\[[Value]]. + 1. Let |returnPromise| be [=a promise resolved with=] |returnResult|.\[[Value]]. + 1. Return the result of [=reacting=] to |returnPromise| with the following fulfillment steps, + given |returnPromiseResult|: + 1. If <a abstract-op>Type</a>(|returnPromiseResult|) is not Object, [=JavaScript/throw=] a + <l spec=ecmascript>{{TypeError}}</l>. + 1. Return <emu-val>undefined</emu-val>. + +</div> + +<div class="example" id="example-js-async-iterable"> + + <code>concatN</code> is an [=operation=] that returns a promise that will be fulfilled with the + concatenation of all the strings yielded by the async iterable passed to it. It stops + concatenating and closes the iterator once the async iterable has yielded N strings. ```suggestion concatenating and closes the iterator once the async iterable has yielded <var ignore>maxN</var> strings. ``` > + The <code>concatN(|iterable|, |maxN|)</code> method steps are: + + 1. Let |promise| be [=a new promise=]. + 1. Let |result| be the empty string. + 1. Let |n| be 0. + 1. Let |iterator| be the result of <a lt="open an async iterable">opening</a> |iterable|. + 1. Let |step| be a sequence of steps that will be used to process the async iterable: + 1. Let |next| be the result of <a lt="get an async iterator next value">getting the next value</a> of |iterator|. + 1. [=React=] to |next|: + - If |next| was fulfilled with value |v|: + 1. If |v| is [=end of iteration=], [=resolve=] |promise| with |result|. + 1. Set |result| to the result of concatenating |result| and |v|. + 1. Set |n| to |n| + 1. + 1. If |n| is |maxN|, then: + 1. Let |finish| be the result of <a lt="close an async iterator">closing</a> + |iterator| with reason <emu-val>undefined</emu-val>. ```suggestion |iterator| with {{undefined}}. ``` (users of these algorithms should be staying in IDL land). > + 1. Let |finish| be the result of <a lt="close an async iterator">closing</a> + |iterator| with reason <emu-val>undefined</emu-val>. + 1. [=React=] to |finish|: + - If |finish| was fulfilled, [=resolve=] |promise| with |result|. + - If |finish| was rejected with reason |r|, [=reject=] |promise| with |r|. + 1. Otherwise: + 1. Call |step|. + - If |next| was rejected with reason |r|, [=reject=] |promise| with |r|. + 1. Call |step|. + 1. Return |promise|. + +</div> +</div> + + +</div> What is this closing? -- Reply to this email directly or view it on GitHub: https://github.com/whatwg/webidl/pull/1397#pullrequestreview-2206732123 You are receiving this because you are subscribed to this thread. Message ID: <whatwg/webidl/pull/1397/review/2206732123@github.com>
Received on Tuesday, 30 July 2024 06:34:54 UTC