From 01223a938cf9cb47b6fac6b495ed633945140038 Mon Sep 17 00:00:00 2001 From: Dominic Farolino Date: Wed, 3 Jan 2024 19:28:18 -0500 Subject: [PATCH 1/4] Some progress --- spec.bs | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 103 insertions(+), 11 deletions(-) diff --git a/spec.bs b/spec.bs index 40121ff..c941108 100644 --- a/spec.bs +++ b/spec.bs @@ -392,33 +392,105 @@ 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

+ +An internal observer is a [=struct=] with the following [=struct/items=]: + +
+ : next steps + :: An algorithm that takes a single parameter. + + : error steps + :: An algorithm that takes a single parameter. + + : complete steps + :: An algorithm with no parameters. +
+ +Note: The [=internal observer=] [=struct=] is used to mirror the {{Observer/next}}, +{{Observer/error}}, and {{Observer/complete}} [=callback functions=], but for "internal" spec prose +that subscribes to an {{Observable}}. This is +as opposed to JavaScript subscribing via the {{Observable/subscribe()}} method, with a +{{ObserverUnion}} packed with Web IDL [=callback functions=]. + +
+ 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 that wishes to subscribe to an + {{Observable}} can do so 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 [[#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=], initialized as: + : [=internal observer/next steps=] + : [=internal observer/error steps=] + : [=internal observer/complete steps=] + :: Algorithms that do nothing. + + 1. Process |observer| as follows: + +
+
If |observer| is an {{ObserverCallback}}
+ +
Set |internal observer|'s [=internal observer/next steps=] to these steps that take an {{any}} |value|:
+
+ + 1. If |observer| is an {{ObserverCallback}}, then set |internal observer|'s [=internal + observer/next steps=] to these steps that take an {{any}} |value|: + + 1. [=Invoke=] |observer| with |value|. - 1. If |observer| is an {{ObserverCallback}}, then set |nextCallback| to |observer|. + If an exception |E| was thrown, then + [=report the exception=] |E|. - 1. Otherwise: + 1. Otherwise, if |observer| is an {{Observer}}: - 1. [=Assert=]: |observer| is an {{Observer}}. + 1. Let |nextCallback| be |observer|'s {{Observer/next}}, |errorCallback| be |observer|'s + {{Observer/error}}, and |completeCallback| be |observer|'s {{Observer/complete}}. - 1. Set |nextCallback| to |observer|'s {{Observer/next}}. + 1. Set |internal observer|'s [=internal observer/next steps=] to these steps that take an {{any}} |value|: - 1. Set |errorCallback| to |observer|'s {{Observer/error}}. + 1. [=Invoke=] |nextCallback| with |value|. - 1. Set |completeCallback| to |observer|'s {{Observer/complete}}. + If an exception |E| was thrown, then + [=report the exception=] |E|. + + 1. Set |internal observer|'s [=internal observer/error steps=] to these steps that take an {{any}} |value|: + + 1. [=Invoke=] |errorCallback| with |value|. + + If an exception |E| was thrown, then + [=report the exception=] |E|. + + 1. Set |internal observer|'s [=internal observer/complete steps=] to these steps: + + 1. [=Invoke=] |completeCallback| with |value|. + + If an exception |E| was thrown, then + [=report the exception=] |E|. 1. Let |subscriber| be a [=new=] {{Subscriber}}, initialized as: : [=Subscriber/next callback=] - :: |nextCallback| + :: |internal observer|'s [=internal observer/next steps=] : [=Subscriber/error callback=] - :: |errorCallback| + :: |internal observer|'s [=internal observer/error steps=] : [=Subscriber/complete callback=] - :: |completeCallback| + :: |internal observer|'s [=internal observer/complete steps=] : [=Subscriber/signal=] :: The result of [=creating a dependent abort signal=] from the list «|subscriber|'s @@ -461,6 +533,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 +586,26 @@ 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 + + : [=internal observer/error steps=] + :: TODO + + : [=internal observer/complete steps=] + :: TODO + + 1. Subscribe to [=this=] given |observer| + + 1. Return |p|. + + 1. TODO: Spec this and use |options| and |p|, and |values|.
From cb1d98dbfb18c6c0585797185fa289b39223740f Mon Sep 17 00:00:00 2001 From: Dominic Farolino Date: Thu, 4 Jan 2024 11:33:02 -0500 Subject: [PATCH 2/4] Probably finish --- spec.bs | 166 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 91 insertions(+), 75 deletions(-) diff --git a/spec.bs b/spec.bs index c941108..3ceeb48 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 a [=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 an 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 an 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 an 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 @@ -398,24 +392,43 @@ callback). The return value of {{EventTarget/on()}} is an example of the latter.

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. + :: An algorithm that takes a single parameter. Initially, these steps do nothing. : error steps - :: An algorithm that takes a single parameter. + :: An algorithm that takes a single parameter. Initially, the [=default error algorithm=]. : complete steps - :: An algorithm with no parameters. + :: An algorithm with no parameters. Initially, these steps do nothing.
-Note: The [=internal observer=] [=struct=] is used to mirror the {{Observer/next}}, -{{Observer/error}}, and {{Observer/complete}} [=callback functions=], but for "internal" spec prose -that subscribes to an {{Observable}}. This is -as opposed to JavaScript subscribing via the {{Observable/subscribe()}} method, with a -{{ObserverUnion}} packed with Web IDL [=callback functions=]. +
+

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=] given by user 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 @@ -423,73 +436,74 @@ as opposed to JavaScript subscribing via the {{Observable/subscribe()}} method, these steps: Note: We split this algorithm out from the Web IDL {{Observable/subscribe()}} method, so that - spec prose that wishes to subscribe to an - {{Observable}} can do so without going through the Web IDL bindings. See 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 [[#operators]] for usage - of this. + 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 |internal observer| be a new [=internal observer=], initialized as: - : [=internal observer/next steps=] - : [=internal observer/error steps=] - : [=internal observer/complete steps=] - :: Algorithms that do nothing. + 1. Let |internal observer| be a new [=internal observer=]. 1. Process |observer| as follows: +
    +
    +
    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 |observer| is an {{ObserverCallback}}
    - -
    Set |internal observer|'s [=internal observer/next steps=] to these steps that take an {{any}} |value|:
    -
    + If an exception |E| was thrown, then + [=report the exception=] |E|. +
    - 1. If |observer| is an {{ObserverCallback}}, then set |internal observer|'s [=internal - observer/next steps=] to these steps that take an {{any}} |value|: +
    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| with |value|. + 1. [=Invoke=] |observer|'s {{Observer/next}} with |value|. - If an exception |E| was thrown, then - [=report the exception=] |E|. + If an exception |E| was thrown, then + [=report the exception=] |E|. - 1. Otherwise, if |observer| is an {{Observer}}: + 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. Let |nextCallback| be |observer|'s {{Observer/next}}, |errorCallback| be |observer|'s - {{Observer/error}}, and |completeCallback| be |observer|'s {{Observer/complete}}. + 1. [=Invoke=] |observer|'s {{Observer/error}} with |error|. - 1. Set |internal observer|'s [=internal observer/next steps=] to these steps that take an {{any}} |value|: + If an exception |E| was thrown, then + [=report the exception=] |E|. - 1. [=Invoke=] |nextCallback| with |value|. + 1. If |observer|'s {{Observer/complete}} is not null, set |internal observer|'s [=internal + observer/complete steps=] to these steps: - If an exception |E| was thrown, then - [=report the exception=] |E|. + 1. [=Invoke=] |observer|'s {{Observer/complete}}. - 1. Set |internal observer|'s [=internal observer/error steps=] to these steps that take an {{any}} |value|: + If an exception |E| was thrown, then + [=report the exception=] |E|. +
    - 1. [=Invoke=] |errorCallback| with |value|. +
    If |observer| is an [=internal observer=]
    +
    Set |internal observer| to |observer|.
    +
    +
- If an exception |E| was thrown, then - [=report the exception=] |E|. - - 1. Set |internal observer|'s [=internal observer/complete steps=] to these steps: - - 1. [=Invoke=] |completeCallback| with |value|. - - If an exception |E| was thrown, then - [=report the exception=] |E|. + 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=] + : [=Subscriber/next algorithm=] :: |internal observer|'s [=internal observer/next steps=] - : [=Subscriber/error callback=] + : [=Subscriber/error algorithm=] :: |internal observer|'s [=internal observer/error steps=] - : [=Subscriber/complete callback=] + : [=Subscriber/complete algorithm=] :: |internal observer|'s [=internal observer/complete steps=] : [=Subscriber/signal=] @@ -593,19 +607,21 @@ For now, see [https://github.com/wicg/observable#operators](https://github.com/w 1. Let |observer| be a new [=internal observer=], initialized as follows: : [=internal observer/next steps=] - :: TODO + :: TODO: Add the value to |values|. : [=internal observer/error steps=] - :: TODO + :: TODO: [=Reject=] |p| with an error. : [=internal observer/complete steps=] - :: TODO + :: 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|. - - 1. TODO: Spec this and use |options| and |p|, and |values|.
From 514de0e169934c8a073e86fd131bcfc01cf15141 Mon Sep 17 00:00:00 2001 From: Dominic Farolino Date: Thu, 4 Jan 2024 11:41:39 -0500 Subject: [PATCH 3/4] Probably fix markdown nit --- spec.bs | 58 +++++++++++++++++++++++++++++---------------------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/spec.bs b/spec.bs index 3ceeb48..6e184bf 100644 --- a/spec.bs +++ b/spec.bs @@ -450,45 +450,47 @@ An internal observer is a [=struct=] with the following [=struct/item 1. Process |observer| as follows:
    -
    -
    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|. +
  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 an exception |E| was thrown, then + [=report the exception=] |E|. +
  2. -
    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|: +
    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|. + 1. [=Invoke=] |observer|'s {{Observer/next}} with |value|. - If an exception |E| was thrown, then - [=report the exception=] |E|. + 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|'s {{Observer/error}} is not null, set |internal observer|'s [=internal + observer/error steps=] to these steps that take an {{any}} |error|: - 1. [=Invoke=] |observer|'s {{Observer/error}} with |error|. + 1. [=Invoke=] |observer|'s {{Observer/error}} with |error|. - If an exception |E| was thrown, then - [=report the exception=] |E|. + If an exception |E| was thrown, then + [=report the exception=] |E|. - 1. If |observer|'s {{Observer/complete}} is not null, set |internal observer|'s [=internal - observer/complete steps=] to these steps: + 1. If |observer|'s {{Observer/complete}} is not null, set |internal observer|'s [=internal + observer/complete steps=] to these steps: - 1. [=Invoke=] |observer|'s {{Observer/complete}}. + 1. [=Invoke=] |observer|'s {{Observer/complete}}. - If an exception |E| was thrown, then - [=report the exception=] |E|. -
    + If an exception |E| was thrown, then + [=report the exception=] |E|. + -
    If |observer| is an [=internal observer=]
    -
    Set |internal observer| to |observer|.
    -
    +
    If |observer| is an [=internal observer=]
    +
    Set |internal observer| to |observer|.
    + +
1. [=Assert=]: |internal observer|'s [=internal observer/error steps=] is either the [=default From 64125f453f0e4fe19b1166b0a13747bd83e0bc35 Mon Sep 17 00:00:00 2001 From: Dominic Farolino Date: Thu, 4 Jan 2024 13:10:45 -0500 Subject: [PATCH 4/4] Small nits and wrapping --- spec.bs | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/spec.bs b/spec.bs index 6e184bf..1255d08 100644 --- a/spec.bs +++ b/spec.bs @@ -164,7 +164,7 @@ interface Subscriber { Each {{Subscriber}} has a next algorithm, which is a [=internal observer/next steps=]-or-null. -Each {{Subscriber}} has a error algorithm, which is a [=internal +Each {{Subscriber}} has a error algorithm, which is an [=internal observer/error steps=]-or-null. Each {{Subscriber}} has a complete algorithm, which is a [=internal @@ -201,7 +201,7 @@ The signal getter steps are to 1. Run [=this=]'s [=Subscriber/next algorithm=] algorithm given |value|. - [=Assert=]: No an exception was thrown. + [=Assert=]: No exception was thrown.
@@ -216,7 +216,7 @@ The signal getter steps are to 1. Run |error algorithm| given |error|. - [=Assert=]: No an exception was thrown. + [=Assert=]: No exception was thrown. 1. [=AbortController/Signal abort=] [=this=]'s [=Subscriber/complete or error controller=].
@@ -233,7 +233,7 @@ The signal getter steps are to 1. Run |complete algorithm|. - [=Assert=]: No an exception was thrown. + [=Assert=]: No exception was thrown. 1. [=AbortController/Signal abort=] [=this=]'s [=Subscriber/complete or error controller=].
@@ -422,7 +422,7 @@ An internal observer is a [=struct=] with the following [=struct/item {{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=] given by user script.

+ {{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 @@ -453,38 +453,40 @@ An internal observer is a [=struct=] with the following [=struct/item

  • If |observer| is an {{ObserverCallback}}
    -
    Set |internal observer|'s [=internal observer/next steps=] to these steps that take an {{any}} |value|: +
    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 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. 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|. + 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|'s {{Observer/error}} is not null, set |internal observer|'s + [=internal observer/error steps=] to these steps that take an {{any}} |error|: 1. [=Invoke=] |observer|'s {{Observer/error}} with |error|. - If an exception |E| was thrown, then - [=report the exception=] |E|. + If an exception |E| was thrown, + then [=report the exception=] |E|. - 1. If |observer|'s {{Observer/complete}} is not null, set |internal observer|'s [=internal - observer/complete steps=] to these steps: + 1. If |observer|'s {{Observer/complete}} is not null, set |internal observer|'s + [=internal observer/complete steps=] to these steps: 1. [=Invoke=] |observer|'s {{Observer/complete}}. - If an exception |E| was thrown, then - [=report the exception=] |E|. + If an exception |E| was thrown, + then [=report the exception=] |E|.
    If |observer| is an [=internal observer=]