Skip to content

Commit

Permalink
add from() spec
Browse files Browse the repository at this point in the history
  • Loading branch information
keithamus authored and domfarolino committed Aug 28, 2024
1 parent 35be035 commit eb51b11
Showing 1 changed file with 152 additions and 3 deletions.
155 changes: 152 additions & 3 deletions spec.bs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ WPT Display: open
urlPrefix: https://tc39.es/ecma262/#; spec: ECMASCRIPT
type: dfn
text: current realm
text: an Object; url: sec-object-type
text: IsPromise; url: sec-ispromise
text: GetMethod; url: sec-getmethod
text: GetIteratorFromMethod; url: sec-getiteratorfrommethod
text: IteratorStepValue; url: sec-iteratorstepvalue
text: normal completion; url: sec-normalcompletion
text: throw completion; url: sec-throwcompletion
urlPrefix: https://dom.spec.whatwg.org; spec: DOM
type: dfn
for: event listener
Expand All @@ -38,6 +45,11 @@ urlPrefix: https://dom.spec.whatwg.org; spec: DOM
text: dependent signals; url: abortsignal-dependent-signals
text: signal abort; url:abortsignal-signal-abort
text: abort reason; url:abortsignal-abort-reason
urlPrefix: https://webidl.spec.whatwg.org; spec: WEBIDL
type: dfn
text: a promise rejected with
type: dfn
text: react
</pre>

<style>
Expand Down Expand Up @@ -371,7 +383,7 @@ interface Observable {
//
// takeUntil() can consume promises, iterables, async iterables, and other
// observables.
Observable takeUntil(any notifier);
Observable takeUntil(any value);
Observable map(Mapper mapper);
Observable filter(Predicate predicate);
Observable take(unsigned long long amount);
Expand Down Expand Up @@ -461,6 +473,133 @@ An <dfn>internal observer</dfn> is a [=struct=] with the following [=struct/item
[[#promise-returning-operators]] that make use of this, for example.</p>
</div>

<div algorithm>
To <dfn for=Observable>convert to an Observable</dfn> given an {{any}} |value|, run these steps:

Note: We split this algorithm out from the Web IDL {{Observable/from()}} method, so that
spec prose can <a for=Observable lt="convert to an observable">convert</a> an {{Observable}}
without going through the Web IDL bindings.

Note: The resolution of value to its descrete types happens before
[=Observable/subscribe callback=] is called. This means mutations of values, such as adding
the iterable protocols to the object, will not take affect between the creation of the returned
observable, and when it is subscribed to.

1. If |value| is not [=an Object=], throw a {{TypeError}}.

Note: This prevents primitive types from being coerced into iterables (e.g. String).

1. If |value| is an {{Observable}}, then return |value|.

1. Let |asyncIteratorMethodRecord| be ? [=GetMethod=](|value|, %Symbol.asyncIterator%).

1. If |asyncIteratorMethodRecord|'s \[[Value]] is not undefined, then:

Note: [=GetMethod=] might return a [=normal completion=] with an undefined value when the
object simply has no asyncIterator method.

1. Let |nextAlgorithm| be the following steps, given |subscriber| and |iterator|:

1. If |iterator|'s \[[Done]] is true, then:

1. Run |subscriber|'s {{Subscriber/complete()}} method and abort these steps.

1. Let |nextRecord| be [=IteratorStepValue=](|iterator|).

1. Let |nextPromise| be undefined.

1. If |nextRecord| is a [=throw completion=] then:

1. Set |nextPromise| to [=a promise rejected with=] |nextRecord|'s \[[Value]].

1. Otherwise, set |nextPromise| to |nextRecord|'s \[[Value]].

1. React to |nextPromise|:

1. If |nextPromise| was fulfilled with value |v|, then:

1. Run |subscriber|'s {{Subscriber/next()}} method, given |v|.

1. Run |nextAlgorithm|, given |subscriber| and |iterator|.

1. If |nextPromise| was rejected with reason |r|, then run |subscriber|'s
{{Subscriber/error()}} method, given |r|.

1. Return a [=new=] {{Observable}} whose [=Observable/subscribe callback=] is an
algorithm that takes a {{Subscriber}} |subscriber| and does the following:

1. Let |iteratorRecord| be [=GetIteratorFromMethod=](|value|, %Symbol.asyncIterator%).

1. If |iteratorRecord| is a [=throw completion=] then:

1. [=queue a microtask=] to perform the following steps:

1. Run |subscriber|'s {{Subscriber/error()}} method, given |iteratorRecord|'s \[[Value]].

1. Otherwise, [=queue a microtask=] to perform the following steps:

1. Run |nextAlgorithm| given |subscriber| and |iteratorRecord|'s \[[Value]].

Note: It is important to [=queue a microtask=] in both branches here to guarantee that
coercing an AsyncIterable never stops the Subscription synchronously, thereby releasing
Zalgo.

1. Let |iteratorMethodRecord| be ? [=GetMethod=](|value|, %Symbol.iterator%).

1. If |iteratorMethodRecord|'s \[[Value]] is not undefined, then:

Note: [=GetMethod=] might return a [=normal completion=] with an undefined value when the
object simply has no asyncIterator method.

1. Return a [=new=] {{Observable}} whose [=Observable/subscribe callback=] is an
algorithm that takes a {{Subscriber}} |subscriber| and does the following:

1. Let |iteratorRecord| be [=GetIteratorFromMethod=](|value|, %Symbol.iterator%).

1. If |iteratorRecord| is a [=throw completion=] then:

1. Run |subscriber|'s {{Subscriber/error()}} method, given |iteratorRecord|'s \[[Value]].

1. Abort these steps.

1. Let |iterator| be |iteratorRecord|'s \[[Value]].

1. Repeat:

1. If |iterator|'s \[[Done]] is true, then:

1. Run |subscriber|'s {{Subscriber/complete()}} method and abort these steps.

1. Let |nextRecord| be [=IteratorStepValue=](|iterator|).

1. If |nextRecord| is a [=throw completion=] then:

1. Run |subscriber|'s {{Subscriber/error()}} method, given |nextRecord|'s \[[Value]].

1. Abort these steps.

1. Run |subscriber|'s {{Subscriber/next()}} given |nextRecord|'s \[[Value]].

1. If [=IsPromise=](|value|) is true, then:

1. Return a [=new=] {{Observable}} whose [=Observable/subscribe callback=] is an
algorithm that takes a {{Subscriber}} |subscriber| and does the following:

1. React to |value|:

1. If |value| was fulfilled with value |v|, then:

1. Run |subscriber|'s {{Subscriber/next()}} method, given |v|.

1. Run |subscriber|'s {{Subscriber/complete()}} method.

1. If |value| was rejected with reason |r|, then run |subscriber|'s
{{Subscriber/error()}} method, given |r|.

1. Throw a {{TypeError}}.

</div>

<div algorithm>
To <dfn for=Observable>subscribe to an {{Observable}}</dfn> given an
{{ObserverUnion}}-or-[=internal observer=] |observer|, and a {{SubscribeOptions}} |options|, run
Expand Down Expand Up @@ -577,15 +716,25 @@ For now, see [https://github.com/wicg/observable#operators](https://github.com/w

<h4 id=observable-from>{{Observable/from()}}</h4>

<p class=XXX>Spec the exact semantics of {{Observable/from()}} conversion.</p>
<div algorithm>
The <dfn for=Observable method><code>from(|value|)</code></dfn> method steps
are:

1. Return the result of <a for=Observable lt="convert to an Observable">
converting</a> |value| to an Observable.

</div>

<h4 id=observable-returning-operators>{{Observable}}-returning operators</h4>

<div algorithm>
The <dfn for=Observable method><code>takeUntil(|notifier|)</code></dfn> method steps are:
The <dfn for=Observable method><code>takeUntil(|value|)</code></dfn> method steps are:

1. Let |sourceObservable| be [=this=].

1. Let |notifier| be the result of <a for=Observable lt="convert to an Observable">
converting</a> |value| to an Observable.

1. Let |observable| be a [=new=] {{Observable}} whose [=Observable/subscribe callback=] is an
algorithm that takes a {{Subscriber}} |subscriber| and does the following:

Expand Down

0 comments on commit eb51b11

Please sign in to comment.