From 35be035423a6234e832f86f4d4da82dcecdb0bae Mon Sep 17 00:00:00 2001 From: Dominic Farolino Date: Wed, 28 Aug 2024 12:07:32 -0400 Subject: [PATCH] Spec the `catch()` operator (#172) --- spec.bs | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/spec.bs b/spec.bs index 69c5ad9..4afbb0a 100644 --- a/spec.bs +++ b/spec.bs @@ -351,6 +351,10 @@ callback Mapper = any (any value, unsigned long long index); // used to visit each element in a sequence, not transform it. callback Visitor = undefined (any value, unsigned long long index); +// This callback returns an `any` that must convert into an `Observable`, via +// the `Observable` conversion semantics. +callback CatchCallback = any (any value); + [Exposed=*] interface Observable { constructor(SubscribeCallback callback); @@ -375,6 +379,7 @@ interface Observable { Observable flatMap(Mapper mapper); Observable switchMap(Mapper mapper); Observable inspect(optional ObservableInspectorUnion inspect_observer = {}); + Observable catch(CatchCallback callback); Observable finally(VoidFunction callback); // Promise-returning operators. @@ -1207,6 +1212,76 @@ For now, see [https://github.com/wicg/observable#operators](https://github.com/w +
+ The catch(|callback|) 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 |sourceObserver| 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=] + :: + 1. [=Invoke=] |callback| with the passed in error. Let |result| be + the returned value. + + If an exception |E| was thrown, then + run |subscriber|'s {{Subscriber/error()}} with |E|, and abort these steps. + + 1. Let |innerObservable| be the result of calling {{Observable/from()}} with |result|. + + If an exception |E| was thrown, then + run |subscriber|'s {{Subscriber/error()}} method, given |E|, and abort these steps. + + Issue: We shouldn't invoke {{Observable/from()}} directly. Rather, we should + call some internal algorithm that passes-back the exceptions for us to handle + properly here, since we want to pipe them to |subscriber|. + + 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. + + : [=internal observer/complete steps=] + :: Run |subscriber|'s {{Subscriber/complete()}} method. + + 1. Let |innerOptions| be a new {{SubscribeOptions}} whose {{SubscribeOptions/signal}} + is |subscriber|'s [=Subscriber/subscription controller=]'s + [=AbortController/signal=]. + + 1. Subscribe to |innerObservable| + given |innerObserver| and |innerOptions|. + + Note: We're free to subscribe to |innerObservable| here without first + "unsubscribing" from |sourceObservable|, and without fear that |sourceObservable| + will keep emitting values, because all of this is happening inside of the [=internal + observer/error steps=] associated with |sourceObservable|. This means + |sourceObservable| has already completed its subscription and will no longer produce + any values, and we are free to safely switch our source of values to |innerObservable|. + + : [=internal observer/complete steps=] + :: Run |subscriber|'s {{Subscriber/complete()}} method. + + 1. Let |options| be a new {{SubscribeOptions}} whose {{SubscribeOptions/signal}} is + |subscriber|'s [=Subscriber/subscription controller=]'s [=AbortController/signal=]. + + 1. Subscribe to |sourceObservable| + given |sourceObserver| and |options|. + + 1. Return |observable|. +
+
The finally(|callback|) method steps are: