Skip to content

Allow async iterators to specify return algorithms #805

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 16, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 84 additions & 5 deletions index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -4310,6 +4310,22 @@ iteration, or resolves with a tuple containing two elements:
1. a value of the first type given in the declaration;
1. a value of the second type given in the declaration.

The prose may also define an <dfn export>asynchronous iterator return</dfn> algorithm. This
algorithm receives the instance of the [=interface=] that is being iterated, the async iterator
itself, and a single argument value of type {{any}}. This algorithm is invoked in the case of
premature termination of the async iterator. It must return a {{Promise}}; if that promise
fulfills, its fulfillment value will be ignored, but if it rejects, that failure will be passed on
to users of the async iterator API.

<p class="note">In the ECMAScript binding, this algorithm allows customizing the behavior when the
async iterator's <code>return()</code> method is invoked. This most commonly occurs when a
<code>break</code> or <code>return</code> statement causes an exit from a
<code>for</code>-<code>await</code>-<code>of</code> loop.

<p class="note">We could add a similar hook for <code>throw()</code>. So far there has been no need,
but if you are creating an API that needs such capabilities, please
<a href="https://github.com/heycam/webidl/issues/new?title=Enhancement%20request%20for%20Async%20Iterables">file an issue</a>.

The prose may also define <dfn export>asynchronous iterator initialization steps</dfn>. These
receive the instance of the [=interface=] being iterated, as well as the newly-created
iterator object.
Expand Down Expand Up @@ -12504,7 +12520,7 @@ A [=default asynchronous iterator object=] has internal values:
values are to be iterated,
* its <dfn for="default asynchronous iterator object">kind</dfn>, which is the iteration kind,
* its <dfn for="default asynchronous iterator object">ongoing promise</dfn>, which is a
{{Promise}} or undefined,
{{Promise}} or null,
* its <dfn for="default asynchronous iterator object">is finished</dfn>, which is a boolean.

Note: [=Default asynchronous iterator objects=] do not have [=class strings=]; when
Expand Down Expand Up @@ -12571,8 +12587,8 @@ The \[[Prototype]] [=internal slot=] of an [=asynchronous iterator prototype obj
[=default asynchronous iterator object/target=] and |object|.
1. Let |fulfillSteps| be the following steps, given |next|:
1. Set |object|'s [=default asynchronous iterator object/ongoing promise=] to
undefined.
1. If |next| is undefined, then:
null.
1. If |next| is <emu-val>undefined</emu-val>, then:
1. Set |object|'s [=default asynchronous iterator object/is finished=] to true.
1. Return [=!=] [$CreateIterResultObject$](<emu-val>undefined</emu-val>,
<emu-val>true</emu-val>).
Expand All @@ -12585,7 +12601,7 @@ The \[[Prototype]] [=internal slot=] of an [=asynchronous iterator prototype obj

1. Let |promise| be |object|'s [=default asynchronous iterator object/ongoing promise=].

1. If |promise| is not undefined, then:
1. If |promise| is not null, then:
1. Let |afterOngoingPromiseCapability| be [=!=] [$NewPromiseCapability$]({{%Promise%}}).
1. Let |onFulfilled| be [=!=] [$CreateBuiltinFunction$](|nextSteps|, « »).
1. Perform [=!=] [$PerformPromiseThen$](|promise|, |onFulfilled|,
Expand All @@ -12600,7 +12616,70 @@ The \[[Prototype]] [=internal slot=] of an [=asynchronous iterator prototype obj
1. Return |object|'s [=default asynchronous iterator object/ongoing promise=].
</div>

Issue: <code>return</code>; <code>throw</code> methods?
<div algorithm="to invoke the return property of asynchronous iterators">

If an [=asynchronous iterator return=] algorithm is defined for the [=inferface=], then the
[=asynchronous iterator prototype object=] must have a <code class="idl">return</code> data
property with attributes { \[[Writable]]: <emu-val>true</emu-val>,
\[[Enumerable]]: <emu-val>true</emu-val>, \[[Configurable]]: <emu-val>true</emu-val> }
and whose value is a [=built-in function object=], taking one argument |value|, that behaves as
follows:

1. Let |interface| be the [=interface=] for which the
[=asynchronous iterator prototype object=] exists.

1. Let |returnPromiseCapability| be [=!=] [$NewPromiseCapability$]({{%Promise%}}).

1. Let |object| be the result of calling [$ToObject$] on the
<emu-val>this</emu-val> value.

1. [$IfAbruptRejectPromise$](|object|, |returnPromiseCapability|).

1. If |object| [=is a platform object=], then [=perform a security check=], passing:
* the platform object |object|,
* the identifier "<code>return</code>", and
* the type "<code>method</code>".

If this threw an exception |e|, then:
1. Perform [=!=] [$Call$](|returnPromiseCapability|.\[[Reject]],
<emu-val>undefined</emu-val>, « |e| »).
1. Return |returnPromiseCapability|.\[[Promise]].

1. If |object| is not a [=default asynchronous iterator object=] for |interface|, then:
1. Let |error| be a new {{ECMAScript/TypeError}}.
1. Perform [=!=] [$Call$](|returnPromiseCapability|.\[[Reject]],
<emu-val>undefined</emu-val>, « |error| »).
1. Return |returnPromiseCapability|.\[[Promise]].

1. If |object|'s [=default asynchronous iterator object/ongoing promise=] is not null, then:
1. Let |error| be a new {{ECMAScript/TypeError}}.
1. Perform [=!=] [$Call$](|returnPromiseCapability|.\[[Reject]],
<emu-val>undefined</emu-val>, « |error| »).
1. Return |returnPromiseCapability|.\[[Promise]].

1. If |object|'s [=default asynchronous iterator object/is finished=] is true, then:
1. Let |result| be [=!=] [$CreateIterResultObject$](|value|,
<emu-val>true</emu-val>).
1. Perform [=!=] [$Call$](|returnPromiseCapability|.\[[Resolve]],
<emu-val>undefined</emu-val>, « |result| »).
1. Return |returnPromiseCapability|.\[[Promise]].

1. Set |object|'s [=default asynchronous iterator object/is finished=] to true.

1. Let |returnPromise| be the result of running the [=asynchronous iterator return=] algorithm
for |interface|, given |object|'s [=default asynchronous iterator object/target=],
|object|, and |value|.

1. Let |fulfillSteps| be the following steps:
1. Return [=!=] [$CreateIterResultObject$](|value|, <emu-val>true</emu-val>).

1. Let |onFulfilled| be [=!=] [$CreateBuiltinFunction$](|fulfillSteps|, « »).

1. Perform [=!=] [$PerformPromiseThen$](|returnPromise|, |onFulfilled|,
<emu-val>undefined</emu-val>, |returnPromiseCapability|).

1. Return |returnPromiseCapability|.\[[Promise]].
</div>

The [=class string=] of an [=asynchronous iterator prototype object=] for a given [=interface=] is
the result of concatenating the [=identifier=] of the [=interface=] and the string
Expand Down