@@ -26,6 +26,13 @@ WPT Display: open
2626urlPrefix: https://tc39.es/ecma262/#; spec: ECMASCRIPT
2727 type: dfn
2828 text: current realm
29+ text: an Object; url: sec-object-type
30+ text: IsPromise; url: sec-ispromise
31+ text: GetMethod; url: sec-getmethod
32+ text: GetIteratorFromMethod; url: sec-getiteratorfrommethod
33+ text: IteratorStepValue; url: sec-iteratorstepvalue
34+ text: normal completion; url: sec-normalcompletion
35+ text: throw completion; url: sec-throwcompletion
2936urlPrefix: https://dom.spec.whatwg.org; spec: DOM
3037 type: dfn
3138 for: event listener
@@ -37,6 +44,11 @@ urlPrefix: https://dom.spec.whatwg.org; spec: DOM
3744 for: AbortSignal
3845 text: dependent signals; url: abortsignal-dependent-signals
3946 text: signal abort; url:abortsignal-signal-abort
47+ urlPrefix: https://webidl.spec.whatwg.org; spec: WEBIDL
48+ type: dfn
49+ text: a promise rejected with
50+ type: dfn
51+ text: react
4052</pre>
4153
4254<style>
@@ -354,7 +366,7 @@ interface Observable {
354366 //
355367 // takeUntil() can consume promises, iterables, async iterables, and other
356368 // observables.
357- Observable takeUntil(any notifier );
369+ Observable takeUntil(any value );
358370 Observable map(Mapper mapper);
359371 Observable filter(Predicate predicate);
360372 Observable take(unsigned long long amount);
@@ -442,6 +454,133 @@ An <dfn>internal observer</dfn> is a [=struct=] with the following [=struct/item
442454 [[#promise-returning-operators]] that make use of this, for example.</p>
443455</div>
444456
457+ <div algorithm>
458+ To <dfn for=Observable>convert to an Observable</dfn> given an {{any}} |value|, run these steps:
459+
460+ Note: We split this algorithm out from the Web IDL {{Observable/from()}} method, so that
461+ spec prose can <a for=Observable lt="convert to an observable">convert</a> an {{Observable}}
462+ without going through the Web IDL bindings.
463+
464+ Note: The resolution of value to its descrete types happens before
465+ [=Observable/subscribe callback=] is called. This means mutations of values, such as adding
466+ the iterable protocols to the object, will not take affect between the creation of the returned
467+ observable, and when it is subscribed to.
468+
469+ 1. If |value| is not [=an Object=] , throw a {{TypeError}} .
470+
471+ Note: This prevents primitive types from being coerced into iterables (e.g. String).
472+
473+ 1. If |value| is an {{Observable}} , then return |value|.
474+
475+ 1. Let |asyncIteratorMethodRecord| be ? [=GetMethod=] (|value|, %Symbol.asyncIterator%).
476+
477+ 1. If |asyncIteratorMethodRecord|'s \[[Value]] is not undefined, then:
478+
479+ Note: [=GetMethod=] might return a [=normal completion=] with an undefined value when the
480+ object simply has no asyncIterator method.
481+
482+ 1. Let |nextAlgorithm| be the following steps, given |subscriber| and |iterator|:
483+
484+ 1. If |iterator|'s \[[Done]] is true, then:
485+
486+ 1. Run |subscriber|'s {{Subscriber/complete()}} method and abort these steps.
487+
488+ 1. Let |nextRecord| be [=IteratorStepValue=] (|iterator|).
489+
490+ 1. Let |nextPromise| be undefined.
491+
492+ 1. If |nextRecord| is a [=throw completion=] then:
493+
494+ 1. Set |nextPromise| to [=a promise rejected with=] |nextRecord|'s \[[Value]] .
495+
496+ 1. Otherwise, set |nextPromise| to |nextRecord|'s \[[Value]] .
497+
498+ 1. React to |nextPromise|:
499+
500+ 1. If |nextPromise| was fulfilled with value |v|, then:
501+
502+ 1. Run |subscriber|'s {{Subscriber/next()}} method, given |v|.
503+
504+ 1. Run |nextAlgorithm|, given |subscriber| and |iterator|.
505+
506+ 1. If |nextPromise| was rejected with reason |r|, then run |subscriber|'s
507+ {{Subscriber/error()}} method, given |r|.
508+
509+ 1. Return a [=new=] {{Observable}} whose [=Observable/subscribe callback=] is an
510+ algorithm that takes a {{Subscriber}} |subscriber| and does the following:
511+
512+ 1. Let |iteratorRecord| be [=GetIteratorFromMethod=] (|value|, %Symbol.asyncIterator%).
513+
514+ 1. If |iteratorRecord| is a [=throw completion=] then:
515+
516+ 1. [=queue a microtask=] to perform the following steps:
517+
518+ 1. Run |subscriber|'s {{Subscriber/error()}} method, given |iteratorRecord|' s \[[Value]] .
519+
520+ 1. Otherwise, [=queue a microtask=] to perform the following steps:
521+
522+ 1. Run |nextAlgorithm| given |subscriber| and |iteratorRecord|'s \[[Value]] .
523+
524+ Note: It is important to [=queue a microtask=] in both branches here to guarantee that
525+ coercing an AsyncIterable never stops the Subscription synchronously, thereby releasing
526+ Zalgo.
527+
528+ 1. Let |iteratorMethodRecord| be ? [=GetMethod=] (|value|, %Symbol.iterator%).
529+
530+ 1. If |iteratorMethodRecord|'s \[[Value]] is not undefined, then:
531+
532+ Note: [=GetMethod=] might return a [=normal completion=] with an undefined value when the
533+ object simply has no asyncIterator method.
534+
535+ 1. Return a [=new=] {{Observable}} whose [=Observable/subscribe callback=] is an
536+ algorithm that takes a {{Subscriber}} |subscriber| and does the following:
537+
538+ 1. Let |iteratorRecord| be [=GetIteratorFromMethod=] (|value|, %Symbol.iterator%).
539+
540+ 1. If |iteratorRecord| is a [=throw completion=] then:
541+
542+ 1. Run |subscriber|'s {{Subscriber/error()}} method, given |iteratorRecord|' s \[[Value]] .
543+
544+ 1. Abort these steps.
545+
546+ 1. Let |iterator| be |iteratorRecord|'s \[[Value]] .
547+
548+ 1. Repeat:
549+
550+ 1. If |iterator|'s \[[Done]] is true, then:
551+
552+ 1. Run |subscriber|'s {{Subscriber/complete()}} method and abort these steps.
553+
554+ 1. Let |nextRecord| be [=IteratorStepValue=] (|iterator|).
555+
556+ 1. If |nextRecord| is a [=throw completion=] then:
557+
558+ 1. Run |subscriber|'s {{Subscriber/error()}} method, given |nextRecord|' s \[[Value]] .
559+
560+ 1. Abort these steps.
561+
562+ 1. Run |subscriber|'s {{Subscriber/next()}} given |nextRecord|' s \[[Value]] .
563+
564+ 1. If [=IsPromise=] (|value|) is true, then:
565+
566+ 1. Return a [=new=] {{Observable}} whose [=Observable/subscribe callback=] is an
567+ algorithm that takes a {{Subscriber}} |subscriber| and does the following:
568+
569+ 1. React to |value|:
570+
571+ 1. If |value| was fulfilled with value |v|, then:
572+
573+ 1. Run |subscriber|'s {{Subscriber/next()}} method, given |v|.
574+
575+ 1. Run |subscriber|'s {{Subscriber/complete()}} method.
576+
577+ 1. If |value| was rejected with reason |r|, then run |subscriber|'s
578+ {{Subscriber/error()}} method, given |r|.
579+
580+ 1. Throw a {{TypeError}} .
581+
582+ </div>
583+
445584<div algorithm>
446585 To <dfn for=Observable>subscribe to an {{Observable}}</dfn> given an
447586 {{ObserverUnion}} -or-[=internal observer=] |observer|, and a {{SubscribeOptions}} |options|, run
@@ -556,15 +695,25 @@ For now, see [https://github.com/wicg/observable#operators](https://github.com/w
556695
557696<h4 id=observable-from>{{Observable/from()}}</h4>
558697
559- <p class=XXX> Spec the exact semantics of {{Observable/from()}} conversion.</p>
698+ <div algorithm>
699+ The <dfn for=Observable method><code>from(|value|)</code></dfn> method steps
700+ are:
701+
702+ 1. Return the result of <a for=Observable lt="convert to an Observable">
703+ converting</a> |value| to an Observable.
704+
705+ </div>
560706
561707<h4 id=observable-returning-operators>{{Observable}}-returning operators</h4>
562708
563709<div algorithm>
564- The <dfn for=Observable method><code>takeUntil(|notifier |)</code></dfn> method steps are:
710+ The <dfn for=Observable method><code>takeUntil(|value |)</code></dfn> method steps are:
565711
566712 1. Let |sourceObservable| be [=this=] .
567713
714+ 1. Let |notifier| be the result of <a for=Observable lt="convert to an Observable">
715+ converting</a> |value| to an Observable.
716+
568717 1. Let |observable| be a [=new=] {{Observable}} whose [=Observable/subscribe callback=] is an
569718 algorithm that takes a {{Subscriber}} |subscriber| and does the following:
570719
0 commit comments