Skip to content

Commit

Permalink
Spec inspect() operator (#168)
Browse files Browse the repository at this point in the history
  • Loading branch information
domfarolino committed Aug 22, 2024
1 parent 34820c9 commit 69ad0de
Showing 1 changed file with 122 additions and 0 deletions.
122 changes: 122 additions & 0 deletions spec.bs
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,19 @@ dictionary SubscriptionObserver {
VoidFunction complete;
};

callback ObservableInspectorAbortHandler = undefined (any value);

dictionary ObservableInspector {
ObservableSubscriptionCallback next;
ObservableSubscriptionCallback error;
VoidFunction complete;

VoidFunction subscribe;
ObservableInspectorAbortHandler abort;
};

typedef (ObservableSubscriptionCallback or SubscriptionObserver) ObserverUnion;
typedef (ObservableSubscriptionCallback or ObservableInspector) ObservableInspectorUnion;

dictionary SubscribeOptions {
AbortSignal signal;
Expand Down Expand Up @@ -362,6 +374,7 @@ interface Observable {
Observable drop(unsigned long long amount);
Observable flatMap(Mapper mapper);
Observable switchMap(Mapper mapper);
Observable inspect(optional ObservableInspectorUnion inspect_observer = {});
Observable finally(VoidFunction callback);

// Promise-returning operators.
Expand Down Expand Up @@ -1085,6 +1098,115 @@ For now, see [https://github.com/wicg/observable#operators](https://github.com/w
|innerObserver| and |innerOptions|.
</div>

<div algorithm>
The <dfn for=Observable method><code>inspect(|inspector_union|)</code></dfn> method steps are:

1. Let |subscribe callback| be a {{VoidFunction}}-or-null, initially null.

1. Let |next callback| be a {{ObservableSubscriptionCallback}}-or-null, initially null.

1. Let |error callback| be a {{ObservableSubscriptionCallback}}-or-null, initially null.

1. Let |complete callback| be a {{VoidFunction}}-or-null, initially null.

1. Let |abort callback| be a {{ObservableInspectorAbortHandler}}-or-null, initially null.

1. Process |inspector_union| as follows:
<dl class="switch">
<dt>If |inspector_union| is an {{ObservableSubscriptionCallback}}</dt>
<dd>
1. Set |next callback| to |inspector_union|.

<dt>If |inspector_union| is an {{ObservableInspector}}</dt>
<dd>
1. If {{ObservableInspector/subscribe}} [=map/exists=] in |inspector_union|, then set
|subscribe callback| to it.

1. If {{ObservableInspector/next}} [=map/exists=] in |inspector_union|, then set
|next callback| to it.

1. If {{ObservableInspector/error}} [=map/exists=] in |inspector_union|, then set
|error callback| to it.

1. If {{ObservableInspector/complete}} [=map/exists=] in |inspector_union|, then set
|complete callback| to it.

1. If {{ObservableInspector/abort}} [=map/exists=] in |inspector_union|, then set
|abort callback| to it.
</dd>
</dl>

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. If |subscribe callback| is not null, then [=invoke=] it.

If <a spec=webidl lt="an exception was thrown">an exception |E| was thrown</a>, then run
|subscriber|'s {{Subscriber/error()}} method, given |E|, and abort these steps.

Note: The result of this is that |sourceObservable| is never subscribed to.

1. If |abort callback| is not null, then [=AbortSignal/add|add the following abort
algorithm=] to |subscriber|'s [=Subscriber/subscription controller=]'s
[=AbortController/signal=]:

1. [=Invoke=] |abort callback| with |subscriber|'s [=Subscriber/subscription
controller=]'s [=AbortController/signal=]'s [=AbortSignal/abort reason=].

If <a spec=webidl lt="an exception was thrown">an exception |E| was thrown</a>, then
[=report the exception=] |E|.

1. Let |sourceObserver| be a new [=internal observer=], initialized as follows:

: [=internal observer/next steps=]
:: 1. If |next callback| is not null, then [=invoke=] |next callback| with the passed in
|value|.

If <a spec=webidl lt="an exception was thrown">an exception |E| was thrown</a>, then:

1. [=AbortSignal/Remove=] |abort callback| from |subscriber|'s
[=Subscriber/subscription controller=]'s [=AbortController/signal=].

Note: This step is important, because the |abort callback| is only meant to be
called for *consumer-initiated* unsubscriptions. When the producer terminates
the subscription (via |subscriber|'s {{Subscriber/error()}} or
{{Subscriber/complete()}} methods) like below, we have to ensure that
|abort callback| is not run.

Issue: This matches Chromium's implementation, but consider holding a reference
to the originally-passed-in {{SubscribeOptions}}'s {{SubscribeOptions/signal}}
and just invoking |abort callback| when *it* aborts. The result is likely the
same, but needs investigation.

1. Run |subscriber|'s {{Subscriber/error()}} method, given |E|, and return.

1. Run |subscriber|'s {{Subscriber/next()}} method with the passed in |value|.

: [=internal observer/error steps=]
:: [=AbortSignal/Remove=] |abort callback| from |subscriber|'s [=Subscriber/subscription
controller=]'s [=AbortController/signal=], and run |subscriber|'s
{{Subscriber/error()}} method, given the passed in <var ignore>error</var>.

: [=internal observer/complete steps=]
:: [=AbortSignal/Remove=] |abort callback| from |subscriber|'s [=Subscriber/subscription
controller=]'s [=AbortController/signal=], and 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. <a for=Observable lt="subscribe to an Observable">Subscribe</a> to |sourceObservable|
given |sourceObserver| and |options|.

1. Return |observable|.

<wpt>
/dom/observable/tentative/observable-inspect.any.js
</wpt>
</div>

<div algorithm>
The <dfn for=Observable method><code>finally(|callback|)</code></dfn> method steps are:

Expand Down

0 comments on commit 69ad0de

Please sign in to comment.