diff --git a/spec.bs b/spec.bs index 40121ff..1255d08 100644 --- a/spec.bs +++ b/spec.bs @@ -161,14 +161,14 @@ interface Subscriber { }; -Each {{Subscriber}} has a next callback, which is an -{{ObserverCallback}}-or-null. +Each {{Subscriber}} has a next algorithm, which is a [=internal +observer/next steps=]-or-null. -Each {{Subscriber}} has a error callback, which is an -{{ObserverCallback}}-or-null. +Each {{Subscriber}} has a error algorithm, which is an [=internal +observer/error steps=]-or-null. -Each {{Subscriber}} has a complete callback, which is a -{{VoidFunction}}-or-null. +Each {{Subscriber}} has a complete algorithm, which is a [=internal +observer/complete steps=]-or-null. Each {{Subscriber}} has a teardown callbacks, which is a [=list=] of {{VoidFunction}}s, initially empty. @@ -199,11 +199,9 @@ The signal getter steps are to 1. If [=this=]'s [=relevant global object=] is a {{Window}} object, and its [=associated Document=] is not [=Document/fully active=], then return. - 1. If [=this=]'s [=Subscriber/next callback=] is non-null, [=invoke=] this's [=Subscriber/next - callback=] with |value|. + 1. Run [=this=]'s [=Subscriber/next algorithm=] algorithm given |value|. - If an exception |E| was thrown, then [=report - the exception=] |E|. + [=Assert=]: No exception was thrown.
@@ -212,16 +210,13 @@ The signal getter steps are to 1. If [=this=]'s [=relevant global object=] is a {{Window}} object, and its [=associated Document=] is not [=Document/fully active=], then return. - 1. Let |callback| be [=this=]'s [=Subscriber/error callback=]. + 1. Let |error algorithm| be [=this=]'s [=Subscriber/error algorithm=]. 1. [=close a subscription|Close=] [=this=]. - 1. If |callback| is not null, [=invoke=] |callback| with |error|. - - If an exception |E| was thrown, then [=report - the exception=] |E|. + 1. Run |error algorithm| given |error|. - 1. Otherwise, [=report the exception=] |error|. + [=Assert=]: No exception was thrown. 1. [=AbortController/Signal abort=] [=this=]'s [=Subscriber/complete or error controller=].
@@ -232,14 +227,13 @@ The signal getter steps are to 1. If [=this=]'s [=relevant global object=] is a {{Window}} object, and its [=associated Document=] is not [=Document/fully active=], then return. - 1. Let |callback| be [=this=]'s [=Subscriber/complete callback=]. + 1. Let |complete algorithm| be [=this=]'s [=Subscriber/complete algorithm=]. 1. [=close a subscription|Close=] [=this=]. - 1. If |callback| is not null, [=invoke=] |callback|. + 1. Run |complete algorithm|. - If an exception |E| was thrown, then [=report - the exception=] |E|. + [=Assert=]: No exception was thrown. 1. [=AbortController/Signal abort=] [=this=]'s [=Subscriber/complete or error controller=]. @@ -264,8 +258,8 @@ The signal getter steps are to 1. Set |subscriber|'s [=Subscriber/active=] boolean to false. - 1. Set |subscriber|'s [=Subscriber/next callback=], [=Subscriber/error callback=], and - [=Subscriber/complete callback=] all to null. + 1. Set |subscriber|'s [=Subscriber/next algorithm=], [=Subscriber/error algorithm=], and + [=Subscriber/complete algorithm=] all to null.

This algorithm intentionally does not have script-running side-effects; it just updates the @@ -392,33 +386,129 @@ callback). The return value of {{EventTarget/on()}} is an example of the latter. The subscribe(|observer|, |options|) method steps are: + 1. Subscribe to [=this=] given |observer| + and |options|. +

+ +

Supporting concepts

+ +
+ The default error algorithm is an algorithm that takes an {{any}} |error|, and runs + these steps: + + 1. [=Report the exception=] |error|. + + Note: We pull this default out separately so that every place in this specification that natively + subscribes to an {{Observable}} (i.e., + subscribes from spec prose, not going through the {{Observable/subscribe()}} method) doesn't have + to redundantly define these steps. +
+ +An internal observer is a [=struct=] with the following [=struct/items=]: + +
+ : next steps + :: An algorithm that takes a single parameter. Initially, these steps do nothing. + + : error steps + :: An algorithm that takes a single parameter. Initially, the [=default error algorithm=]. + + : complete steps + :: An algorithm with no parameters. Initially, these steps do nothing. +
+ +
+

The [=internal observer=] [=struct=] is used to mirror the {{Observer/next}}, + {{Observer/error}}, and {{Observer/complete}} [=callback functions=]. For any {{Observable}} that + is subscribed by JavaScript via the {{Observable/subscribe()}} method, these algorithm "steps" + will just be a wrapper around [=invoking=] the corresponding {{Observer/next}}, + {{Observer/error}}, and {{Observer/complete}} [=callback functions=] provided by script.

+ +

But when internal spec prose (not user script) subscribes to an {{Observable}}, these "steps" are arbitrary spec algorithms that + are not provided via an {{ObserverUnion}} packed with Web IDL [=callback functions=]. See the + [[#promise-returning-operators]] that make use of this, for example.

+
+ +
+ To subscribe to an {{Observable}} given an + {{ObserverUnion}}-or-[=internal observer=] |observer|, and a {{SubscribeOptions}} |options|, run + these steps: + + Note: We split this algorithm out from the Web IDL {{Observable/subscribe()}} method, so that + spec prose can subscribe to an + {{Observable}} without going through the Web IDL bindings. See w3c/IntersectionObserver#464 for + similar context, where "internal" prose must not go through Web IDL + bindings on objects whose properties could be mutated by JavaScript. See + [[#promise-returning-operators]] for usage of this. + 1. If [=this=]'s [=relevant global object=] is a {{Window}} object, and its [=associated Document=] is not [=Document/fully active=], then return. - 1. Let |nextCallback|, |errorCallback|, and |completeCallback| all be null. + 1. Let |internal observer| be a new [=internal observer=]. + + 1. Process |observer| as follows: +
    +
  1. +
    +
    If |observer| is an {{ObserverCallback}}
    +
    Set |internal observer|'s [=internal observer/next steps=] to these steps that take + an {{any}} |value|: + + 1. [=Invoke=] |observer| with |value|. + + If an exception |E| was thrown, + then [=report the exception=] |E|. +
    + +
    If |observer| is an {{Observer}}
    +
    + 1. If |observer|'s {{Observer/next}} is not null, set |internal observer|'s + [=internal observer/next steps=] to these steps that take an {{any}} |value|: + + 1. [=Invoke=] |observer|'s {{Observer/next}} with |value|. + + If an exception |E| was thrown, + then [=report the exception=] |E|. + + 1. If |observer|'s {{Observer/error}} is not null, set |internal observer|'s + [=internal observer/error steps=] to these steps that take an {{any}} |error|: - 1. If |observer| is an {{ObserverCallback}}, then set |nextCallback| to |observer|. + 1. [=Invoke=] |observer|'s {{Observer/error}} with |error|. - 1. Otherwise: + If an exception |E| was thrown, + then [=report the exception=] |E|. - 1. [=Assert=]: |observer| is an {{Observer}}. + 1. If |observer|'s {{Observer/complete}} is not null, set |internal observer|'s + [=internal observer/complete steps=] to these steps: - 1. Set |nextCallback| to |observer|'s {{Observer/next}}. + 1. [=Invoke=] |observer|'s {{Observer/complete}}. - 1. Set |errorCallback| to |observer|'s {{Observer/error}}. + If an exception |E| was thrown, + then [=report the exception=] |E|. +
    - 1. Set |completeCallback| to |observer|'s {{Observer/complete}}. +
    If |observer| is an [=internal observer=]
    +
    Set |internal observer| to |observer|.
    +
    +
  2. +
+ + 1. [=Assert=]: |internal observer|'s [=internal observer/error steps=] is either the [=default + error algorithm=], or an algorithm that [=invokes=] the provided {{Observer/error}} + [=callback function=]. 1. Let |subscriber| be a [=new=] {{Subscriber}}, initialized as: - : [=Subscriber/next callback=] - :: |nextCallback| + : [=Subscriber/next algorithm=] + :: |internal observer|'s [=internal observer/next steps=] - : [=Subscriber/error callback=] - :: |errorCallback| + : [=Subscriber/error algorithm=] + :: |internal observer|'s [=internal observer/error steps=] - : [=Subscriber/complete callback=] - :: |completeCallback| + : [=Subscriber/complete algorithm=] + :: |internal observer|'s [=internal observer/complete steps=] : [=Subscriber/signal=] :: The result of [=creating a dependent abort signal=] from the list «|subscriber|'s @@ -461,6 +551,7 @@ callback). The return value of {{EventTarget/on()}} is an example of the latter. |subscriber|.
+

Operators

For now, see [https://github.com/wicg/observable#operators](https://github.com/wicg/observable#operators). @@ -513,7 +604,28 @@ For now, see [https://github.com/wicg/observable#operators](https://github.com/w
The toArray(|options|) method steps are: - 1. TODO: Spec this and use |options|. + 1. Let |p| [=a new promise=]. + + 1. Let |values| be a new [=list=]. + + 1. Let |observer| be a new [=internal observer=], initialized as follows: + + : [=internal observer/next steps=] + :: TODO: Add the value to |values|. + + : [=internal observer/error steps=] + :: TODO: [=Reject=] |p| with an error. + + : [=internal observer/complete steps=] + :: TODO: [=Resolve=] |p| with |values|. + + 1. TODO: Finish the actual spec for this method and use |options|'s + {{PromiseOptions/signal}} to [=reject=] |p| appropriately. + + 1. Subscribe to [=this=] given |observer| + and |options|. + + 1. Return |p|.