Skip to content

Commit 75d1e16

Browse files
authored
Spec the switchMap operator (#130)
1 parent b4b9d43 commit 75d1e16

File tree

1 file changed

+123
-0
lines changed

1 file changed

+123
-0
lines changed

spec.bs

+123
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,7 @@ interface Observable {
366366
Observable take(unsigned long long amount);
367367
Observable drop(unsigned long long amount);
368368
Observable flatMap(Mapper mapper);
369+
Observable switchMap(Mapper mapper);
369370
Observable finally(VoidFunction callback);
370371

371372
// Promise-returning operators.
@@ -976,6 +977,128 @@ For now, see [https://github.com/wicg/observable#operators](https://github.com/w
976977
|innerObserver| and |innerOptions|.
977978
</div>
978979

980+
<div algorithm>
981+
The <dfn for=Observable method><code>switchMap(|mapper|)</code></dfn> method steps are:
982+
983+
1. Let |sourceObservable| be [=this=].
984+
985+
1. Let |observable| be a [=new=] {{Observable}} whose [=Observable/subscribe callback=] is an
986+
algorithm that takes a {{Subscriber}} |subscriber| and does the following:
987+
988+
1. Let |idx| be an {{unsigned long long}}, initially 0.
989+
990+
1. Let |outerSubscriptionHasCompleted| be a [=boolean=], initially false.
991+
992+
1. Let |activeInnerAbortController| be an {{AbortController}}-or-null, initially null.
993+
994+
Note: This {{AbortController}} is assigned to a new {{AbortController}} only by this
995+
algorithm's <a href=#switchmap-next-steps>next steps</a> (below), and only assigned to
996+
null by the [=switchmap process next value steps=], when the "inner" {{Observable}} either
997+
completes or errors. This variable is used as a marker for whether there is currently an
998+
active "inner" subscription. The <a href=#switchmap-complete-steps>complete steps</a>
999+
below care about this, because if |sourceObservable| completes while there is an active
1000+
"inner" subscription, we do not immediately complete |subscriber|. In that case,
1001+
|subscriber|'s completion becomes blocked on the "inner" subscription's completion.
1002+
1003+
1. Let |sourceObserver| be a new [=internal observer=], initialized as follows:
1004+
1005+
: <dt id=switchmap-next-steps>[=internal observer/next steps=]</dt>
1006+
:: 1. If |activeInnerAbortController| is not null, then [=AbortController/signal abort=]
1007+
|activeInnerAbortController|.
1008+
1009+
Note: This "unsubscribes" from the "inner" {{Observable}} that was derived from the
1010+
value that was <i>last</i> pushed from |sourceObservable|. Then we immediately
1011+
subscribe to the <i>new</i> {{Observable}} that we're about to derive from |value|,
1012+
i.e., the <i>most-recently</i> pushed value from |sourceObservable|.
1013+
1014+
1. Set |activeInnerAbortController| to a [=new=] {{AbortController}}.
1015+
1016+
1. Run the [=switchmap process next value steps=] with |value|, |subscriber|, |mapper|,
1017+
and <b>references</b> to all of the following: |activeInnerAbortController|,
1018+
|outerSubscriptionHasCompleted|, and |idx|.
1019+
1020+
Note: The [=switchmap process next value steps=] will subscribe to the
1021+
{{Observable}} derived from |value| (if one such can be derived) and keep processing
1022+
values from it until either (1) its subscription becomes inactive (either by error or
1023+
completion), or (2) |activeInnerAbortController| <a href=#switchmap-next-steps>gets
1024+
aborted</a>, due to |sourceObservable| pushing another <i>newer</i> value that will
1025+
replace the current "inner" subscription.
1026+
1027+
: [=internal observer/error steps=]
1028+
:: Run |subscriber|'s {{Subscriber/error()}} method, given the passed in <var
1029+
ignore>error</var>.
1030+
1031+
: <dt id=switchmap-complete-steps>[=internal observer/complete steps=]</dt>
1032+
:: 1. Set |outerSubscriptionHasCompleted| to true.
1033+
1034+
Note: If |activeInnerAbortController| is not null, then we don't immediately
1035+
complete |subscriber|. Instead, the [=switchmap process next value steps=] will
1036+
complete |subscriber| when the inner subscription finally completes itself.
1037+
1038+
1. If |activeInnerAbortController| is null, run |subscriber|'s
1039+
{{Subscriber/complete()}} method.
1040+
1041+
1. Let |options| be a new {{SubscribeOptions}} whose {{SubscribeOptions/signal}} is
1042+
|subscriber|'s [=Subscriber/signal=].
1043+
1044+
1. <a for=Observable lt="subscribe to an Observable">Subscribe</a> to |sourceObservable|
1045+
given |sourceObserver| and |options|.
1046+
1047+
1. Return |observable|.
1048+
</div>
1049+
1050+
<div algorithm>
1051+
The <dfn>switchmap process next value steps</dfn>, given an {{any}} |value|, a {{Subscriber}}
1052+
|subscriber|, a {{Mapper}} |mapper|, and <b>references</b> to all of the following: an
1053+
{{AbortController}} |activeInnerAbortController|, a [=boolean=]
1054+
|outerSubscriptionHasCompleted|, and an {{unsigned long long}} |idx| are to run these steps:
1055+
1056+
1. Let |mappedResult| be the result of [=invoking=] |mapper| with |value| and |idx|.
1057+
1058+
If <a spec=webidl lt="an exception was thrown">an exception |E| was thrown</a>, then run
1059+
|subscriber|'s {{Subscriber/error()}} method, given |E|, and abort these steps.
1060+
1061+
1. Set |idx| to |idx| + 1.
1062+
1063+
1. Let |innerObservable| be the result of calling {{Observable/from()}} with |mappedResult|.
1064+
1065+
If <a spec=webidl lt="an exception was thrown">an exception |E| was thrown</a>, then run
1066+
|subscriber|'s {{Subscriber/error()}} method, given |E|, and abort these steps.
1067+
1068+
1. Let |innerObserver| be a new [=internal observer=], initialized as follows:
1069+
1070+
: [=internal observer/next steps=]
1071+
:: Run |subscriber|'s {{Subscriber/next()}} method, given the passed in |value|.
1072+
1073+
: [=internal observer/error steps=]
1074+
:: Run |subscriber|'s {{Subscriber/error()}} method, given the passed in <var
1075+
ignore>error</var>.
1076+
1077+
Note: We don't have to set |activeInnerAbortController| to null here, to signal to the
1078+
{{Observable/switchMap()}} method steps above that the inner "subscription" has been
1079+
canceled. That's because calling |subscriber|'s {{Subscriber/error()}} method already
1080+
unsubscribes from the "outer" source Observable, so it will not be able to push any more
1081+
values to the {{Observable/switchMap()}} internal observer.
1082+
1083+
: [=internal observer/complete steps=]
1084+
:: 1. If |outerSubscriptionHasCompleted| is true, run |subscriber|'s
1085+
{{Subscriber/complete()}} method.
1086+
1087+
1. Otherwise, set |activeInnerAbortController| to null.
1088+
1089+
Note: Because this variable is a reference, it signals to the <a
1090+
href=#switchmap-complete-steps>switchMap complete steps</a> that there is no active
1091+
inner subscription.
1092+
1093+
1. Let |innerOptions| be a new {{SubscribeOptions}} whose {{SubscribeOptions/signal}} is the
1094+
result of [=creating a dependent abort signal=] from the list
1095+
«|activeInnerAbortController|'s [=AbortController/signal=], |subscriber|'s
1096+
[=Subscriber/signal=]», using {{AbortSignal}}, and the [=current realm=].
1097+
1098+
1. <a for=Observable lt="subscribe to an Observable">Subscribe</a> to |innerObservable| given
1099+
|innerObserver| and |innerOptions|.
1100+
</div>
1101+
9791102
<div algorithm>
9801103
The <dfn for=Observable method><code>finally(|callback|)</code></dfn> method steps are:
9811104

0 commit comments

Comments
 (0)