@@ -26,6 +26,12 @@ WPT Display: open
2626urlPrefix: https://tc39.es/ecma262/#; spec: ECMASCRIPT
2727 type: dfn
2828 text: current realm
29+ text: IsPromise; url: sec-ispromise
30+ text: GetMethod; url: sec-getmethod
31+ text: GetIteratorFromMethod; url: sec-getiteratorfrommethod
32+ text: IteratorStepValue; url: sec-iteratorstepvalue
33+ text: normal completion; url: sec-normalcompletion
34+ text: throw completion; url: sec-throwcompletion
2935urlPrefix: https://dom.spec.whatwg.org; spec: DOM
3036 type: dfn
3137 for: event listener
@@ -37,6 +43,13 @@ urlPrefix: https://dom.spec.whatwg.org; spec: DOM
3743 for: AbortSignal
3844 text: dependent signals; url: abortsignal-dependent-signals
3945 text: signal abort; url:abortsignal-signal-abort
46+ urlPrefix: https://webidl.spec.whatwg.org; spec: WEBIDL
47+ type: dfn
48+ text: a promise rejected with
49+ type: dfn
50+ text: Upon fulfillment
51+ type: dfn
52+ text: Upon rejection
4053</pre>
4154
4255<style>
@@ -354,7 +367,7 @@ interface Observable {
354367 //
355368 // takeUntil() can consume promises, iterables, async iterables, and other
356369 // observables.
357- Observable takeUntil(any notifier );
370+ Observable takeUntil(any value );
358371 Observable map(Mapper mapper);
359372 Observable filter(Predicate predicate);
360373 Observable take(unsigned long long amount);
@@ -442,6 +455,130 @@ An <dfn>internal observer</dfn> is a [=struct=] with the following [=struct/item
442455 [[#promise-returning-operators]] that make use of this, for example.</p>
443456</div>
444457
458+ <div algorithm>
459+ To <dfn for=Observable>convert to an Observable</dfn> given an {{any}} |value|, run these steps:
460+
461+ Note: We split this algorithm out from the Web IDL {{Observable/from()}} method, so that
462+ spec prose can <a for=Observable lt="convert to an observable">convert</a> an {{Observable}}
463+ without going through the Web IDL bindings.
464+
465+ Note: The resolution of value to its descrete types happens before
466+ [=Observable/subscribe callback=] is called. This means mutations of values, such as adding
467+ the iterable protocols to the object, will not take affect between the creation of the returned
468+ observable, and when it is subscribed to.
469+
470+ 1. If |value| is an {{Observable}} , then return |value|.
471+
472+ 1. Let |asyncIteratorMethodRecord| be [=GetMethod=] (|value|, %Symbol.asyncIterator%).
473+
474+ 1. If |asyncIteratorMethodRecord| is a [=normal completion=] and
475+ |asyncIteratorMethodRecord|'s \[[Value]] is not undefined, then:
476+
477+ Note: [=GetMethod=] may return a [=normal completion=] with an undefined value when the object
478+ simply has no asyncIterator method.
479+
480+ 1. Let |nextAlgorithm| be the following steps, given |subscriber| and |iterator|:
481+
482+ 1. If |iterator|'s \[[Done]] is true, then:
483+
484+ 1. Run |subscriber|'s {{Subscriber/complete()}} method and abort these steps.
485+
486+ 1. Let |nextRecord| be [=IteratorStepValue=] (|iterator|).
487+
488+ 1. Let |nextPromise| be undefined.
489+
490+ 1. If |nextRecord| is a [=throw completion=] then:
491+
492+ 1. Set |nextPromise| to [=a promise rejected with=] |nextRecord|'s \[[Value]] .
493+
494+ 1. Otherwise, set |nextPromise| to |nextRecord|'s \[[Value]] .
495+
496+ 1. [=Upon fulfillment=] of |nextPromise|, run the following steps, given |resolution|:
497+
498+ 1. Run |subscriber|'s {{Subscriber/next()}} method, given |resolution|.
499+
500+ 1. Run |nextAlgorithm|, given |subscriber| and |iterator|.
501+
502+ 1. [=Upon rejection=] of |nextPromise|, run the following steps, given |rejection|:
503+
504+ 1. Run |subscriber|'s {{Subscriber/error()}} method, given |rejection|.
505+
506+ 1. Return a [=new=] {{Observable}} whose [=Observable/subscribe callback=] is an
507+ algorithm that takes a {{Subscriber}} |subscriber| and does the following:
508+
509+ 1. Let |iteratorRecord| be [=GetIteratorFromMethod=] (|value|, %Symbol.asyncIterator%).
510+
511+ 1. If |iteratorRecord| is a [=throw completion=] then:
512+
513+ 1. [=queue a microtask=] to perform the following steps:
514+
515+ 1. Run |subscriber|'s {{Subscriber/error()}} method, given |iteratorRecord|' s \[[Value]] .
516+
517+ 1. Otherwise, [=queue a microtask=] to perform the following steps:
518+
519+ 1. Run |nextAlgorithm| given |subscriber| and |iteratorRecord|'s \[[Value]] .
520+
521+ Note: It is important to [=queue a microtask=] in both branches here to guarantee that
522+ coercing an AsyncIterable never stops the Subscription synchronously, thereby releasing
523+ Zalgo.
524+
525+ 1. Let |iteratorMethodRecord| be [=GetMethod=] (|value|, %Symbol.iterator%).
526+
527+ 1. If |iteratorMethodRecord| is a [=normal completion=] and
528+ |iteratorMethodRecord|'s \[[Value]] is not undefined, then:
529+
530+ Note: [=GetMethod=] may return a [=normal completion=] with an undefined value when the object
531+ simply has no asyncIterator method.
532+
533+ 1. Return a [=new=] {{Observable}} whose [=Observable/subscribe callback=] is an
534+ algorithm that takes a {{Subscriber}} |subscriber| and does the following:
535+
536+ 1. Let |iteratorRecord| be [=GetIteratorFromMethod=] (|value|, %Symbol.iterator%).
537+
538+ 1. If |iteratorRecord| is a [=throw completion=] then:
539+
540+ 1. Run |subscriber|'s {{Subscriber/error()}} method, given |iteratorRecord|' s \[[Value]] .
541+
542+ 1. Abort these steps.
543+
544+ 1. Let |iterator| be |iteratorRecord|'s \[[Value]] .
545+
546+ 1. Repeat:
547+
548+ 1. If |iterator|'s \[[Done]] is true, then:
549+
550+ 1. Run |subscriber|'s {{Subscriber/complete()}} method and abort these steps.
551+
552+ 1. Let |nextRecord| be [=IteratorStepValue=] (|iterator|).
553+
554+ 1. If |nextRecord| is a [=throw completion=] then:
555+
556+ 1. Run |subscriber|'s {{Subscriber/error()}} method, given |nextRecord|' s \[[Value]] .
557+
558+ 1. Abort these steps.
559+
560+ 1. Run |subscriber|'s {{Subscriber/next()}} given |nextRecord|' s \[[Value]] .
561+
562+ 1. If [=IsPromise=] (|value|) is true, then:
563+
564+ 1. Return a [=new=] {{Observable}} whose [=Observable/subscribe callback=] is an
565+ algorithm that takes a {{Subscriber}} |subscriber| and does the following:
566+
567+ 1. [=Upon fulfillment=] of |value|, run the following steps, given |resolution|:
568+
569+ 1. Run |subscriber|'s {{Subscriber/next()}} method, given |resolution|.
570+
571+ 1. Run |subscriber|'s {{Subscriber/complete()}} method.
572+
573+ 1. [=Upon rejection=] of |value|, run the following steps, given |rejection|:
574+
575+ 1. Run |subscriber|'s {{Subscriber/error()}} method, given |rejection|.
576+
577+ 1. Throw a {{TypeError}} .
578+
579+ </div>
580+
581+
445582<div algorithm>
446583 To <dfn for=Observable>subscribe to an {{Observable}}</dfn> given an
447584 {{ObserverUnion}} -or-[=internal observer=] |observer|, and a {{SubscribeOptions}} |options|, run
@@ -556,15 +693,25 @@ For now, see [https://github.com/wicg/observable#operators](https://github.com/w
556693
557694<h4 id=observable-from>{{Observable/from()}}</h4>
558695
559- <p class=XXX> Spec the exact semantics of {{Observable/from()}} conversion.</p>
696+ <div algorithm>
697+ The <dfn for=Observable method><code>from(|value|)</code></dfn> method steps
698+ are:
699+
700+ 1. Return the result of <a for=Observable lt="convert to an Observable">
701+ converting</a> |value| to an Observable.
702+
703+ </div>
560704
561705<h4 id=observable-returning-operators>{{Observable}}-returning operators</h4>
562706
563707<div algorithm>
564- The <dfn for=Observable method><code>takeUntil(|notifier |)</code></dfn> method steps are:
708+ The <dfn for=Observable method><code>takeUntil(|value |)</code></dfn> method steps are:
565709
566710 1. Let |sourceObservable| be [=this=] .
567711
712+ 1. Let |notifier| be the result of <a for=Observable lt="convert to an Observable">
713+ converting</a> |value| to an Observable.
714+
568715 1. Let |observable| be a [=new=] {{Observable}} whose [=Observable/subscribe callback=] is an
569716 algorithm that takes a {{Subscriber}} |subscriber| and does the following:
570717
0 commit comments