@@ -366,6 +366,7 @@ interface Observable {
366
366
Observable take(unsigned long long amount);
367
367
Observable drop(unsigned long long amount);
368
368
Observable flatMap(Mapper mapper);
369
+ Observable switchMap(Mapper mapper);
369
370
Observable finally(VoidFunction callback);
370
371
371
372
// Promise-returning operators.
@@ -976,6 +977,128 @@ For now, see [https://github.com/wicg/observable#operators](https://github.com/w
976
977
|innerObserver| and |innerOptions|.
977
978
</div>
978
979
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
+
979
1102
<div algorithm>
980
1103
The <dfn for=Observable method><code>finally(|callback|)</code></dfn> method steps are:
981
1104
0 commit comments