diff --git a/spec.bs b/spec.bs
index 24c0651..58994eb 100644
--- a/spec.bs
+++ b/spec.bs
@@ -352,6 +352,7 @@ interface Observable {
Observable take(unsigned long long amount);
Observable drop(unsigned long long amount);
Observable flatMap(Mapper mapper);
+ Observable switchMap(Mapper mapper);
Observable finally(VoidFunction callback);
// Promise-returning operators.
@@ -958,6 +959,128 @@ For now, see [https://github.com/wicg/observable#operators](https://github.com/w
|innerObserver| and |innerOptions|.
+
+ The
switchMap(|mapper|)
method steps are:
+
+ 1. Let |sourceObservable| be [=this=].
+
+ 1. Let |observable| be a [=new=] {{Observable}} whose [=Observable/subscribe callback=] is an
+ algorithm that takes a {{Subscriber}} |subscriber| and does the following:
+
+ 1. Let |idx| be an {{unsigned long long}}, initially 0.
+
+ 1. Let |outerSubscriptionHasCompleted| be a [=boolean=], initially false.
+
+ 1. Let |activeInnerAbortController| be an {{AbortController}}-or-null, initially null.
+
+ Note: This {{AbortController}} is assigned to a new {{AbortController}} only by this
+ algorithm's
next steps (below), and only assigned to
+ null by the [=switchmap process next value steps=], when the "inner" {{Observable}} either
+ completes or errors. This variable is used as a marker for whether there is currently an
+ active "inner" subscription. The
complete steps
+ below care about this, because if |sourceObservable| completes while there is an active
+ "inner" subscription, we do not immediately complete |subscriber|. In that case,
+ |subscriber|'s completion becomes blocked on the "inner" subscription's completion.
+
+ 1. Let |sourceObserver| be a new [=internal observer=], initialized as follows:
+
+ :
[=internal observer/next steps=]
+ :: 1. If |activeInnerAbortController| is not null, then [=AbortController/signal abort=]
+ |activeInnerAbortController|.
+
+ Note: This "unsubscribes" from the "inner" {{Observable}} that was derived from the
+ value that was
last pushed from |sourceObservable|. Then we immediately
+ subscribe to the
new {{Observable}} that we're about to derive from |value|,
+ i.e., the
most-recently pushed value from |sourceObservable|.
+
+ 1. Set |activeInnerAbortController| to a [=new=] {{AbortController}}.
+
+ 1. Run the [=switchmap process next value steps=] with |value|, |subscriber|, |mapper|,
+ and
references to all of the following: |activeInnerAbortController|,
+ |outerSubscriptionHasCompleted|, and |idx|.
+
+ Note: The [=switchmap process next value steps=] will subscribe to the
+ {{Observable}} derived from |value| (if one such can be derived) and keep processing
+ values from it until either (1) its subscription becomes inactive (either by error or
+ completion), or (2) |activeInnerAbortController|
gets
+ aborted, due to |sourceObservable| pushing another
newer value that will
+ replace the current "inner" subscription.
+
+ : [=internal observer/error steps=]
+ :: Run |subscriber|'s {{Subscriber/error()}} method, given the passed in
error.
+
+ :
[=internal observer/complete steps=]
+ :: 1. Set |outerSubscriptionHasCompleted| to true.
+
+ Note: If |activeInnerAbortController| is not null, then we don't immediately
+ complete |subscriber|. Instead, the [=switchmap process next value steps=] will
+ complete |subscriber| when the inner subscription finally completes itself.
+
+ 1. If |activeInnerAbortController| is null, run |subscriber|'s
+ {{Subscriber/complete()}} method.
+
+ 1. Let |options| be a new {{SubscribeOptions}} whose {{SubscribeOptions/signal}} is
+ |subscriber|'s [=Subscriber/signal=].
+
+ 1.
Subscribe to |sourceObservable|
+ given |sourceObserver| and |options|.
+
+ 1. Return |observable|.
+
+
+
+ The
switchmap process next value steps, given an {{any}} |value|, a {{Subscriber}}
+ |subscriber|, a {{Mapper}} |mapper|, and
references to all of the following: an
+ {{AbortController}} |activeInnerAbortController|, a [=boolean=]
+ |outerSubscriptionHasCompleted|, and an {{unsigned long long}} |idx| are to run these steps:
+
+ 1. Let |mappedResult| be the result of [=invoking=] |mapper| with |value| and |idx|.
+
+ If
an exception |E| was thrown, then run
+ |subscriber|'s {{Subscriber/error()}} method, given |E|, and abort these steps.
+
+ 1. Set |idx| to |idx| + 1.
+
+ 1. Let |innerObservable| be the result of calling {{Observable/from()}} with |mappedResult|.
+
+ If
an exception |E| was thrown, then run
+ |subscriber|'s {{Subscriber/error()}} method, given |E|, and abort these steps.
+
+ 1. Let |innerObserver| be a new [=internal observer=], initialized as follows:
+
+ : [=internal observer/next steps=]
+ :: Run |subscriber|'s {{Subscriber/next()}} method, given the passed in |value|.
+
+ : [=internal observer/error steps=]
+ :: Run |subscriber|'s {{Subscriber/error()}} method, given the passed in
error.
+
+ Note: We don't have to set |activeInnerAbortController| to null here, to signal to the
+ {{Observable/switchMap()}} method steps above that the inner "subscription" has been
+ canceled. That's because calling |subscriber|'s {{Subscriber/error()}} method already
+ unsubscribes from the "outer" source Observable, so it will not be able to push any more
+ values to the {{Observable/switchMap()}} internal observer.
+
+ : [=internal observer/complete steps=]
+ :: 1. If |outerSubscriptionHasCompleted| is true, run |subscriber|'s
+ {{Subscriber/complete()}} method.
+
+ 1. Otherwise, set |activeInnerAbortController| to null.
+
+ Note: Because this variable is a reference, it signals to the
switchMap complete steps that there is no active
+ inner subscription.
+
+ 1. Let |innerOptions| be a new {{SubscribeOptions}} whose {{SubscribeOptions/signal}} is the
+ result of [=creating a dependent abort signal=] from the list
+ «|activeInnerAbortController|'s [=AbortController/signal=], |subscriber|'s
+ [=Subscriber/signal=]», using {{AbortSignal}}, and the [=current realm=].
+
+ 1.
Subscribe to |innerObservable| given
+ |innerObserver| and |innerOptions|.
+
+
The finally(|callback|)
method steps are: