Skip to content

Commit a091c5a

Browse files
authored
Spec ref-counted producers (#197)
1 parent df571e8 commit a091c5a

File tree

1 file changed

+67
-25
lines changed

1 file changed

+67
-25
lines changed

spec.bs

Lines changed: 67 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,8 @@ interface Subscriber {
177177
};
178178
</xmp>
179179

180-
Each {{Subscriber}} has a <dfn for=Subscriber>internal observer</dfn>, which is an [=internal
181-
observer=].
180+
Each {{Subscriber}} has an [=ordered set=] of <dfn for=Subscriber>internal observers</dfn>,
181+
initially empty.
182182

183183
Each {{Subscriber}} has a <dfn for=Subscriber>teardown callbacks</dfn>, which is a [=list=] of
184184
{{VoidFunction}}s, initially empty.
@@ -205,21 +205,22 @@ The <dfn attribute for=Subscriber><code>signal</code></dfn> getter steps are to
205205
1. If [=this=]'s [=relevant global object=] is a {{Window}} object, and its [=associated
206206
Document=] is not [=Document/fully active=], then return.
207207

208-
1. Run [=this=]'s [=Subscriber/internal observer=]'s [=internal observer/next steps=] given
209-
|value|.
208+
1. [=set/For each=] |observer| of [=this=]'s [=Subscriber/internal observers=]:
210209

211-
[=Assert=]: No <a spec=webidl lt="an exception was thrown">exception was thrown</a>.
210+
1. Run |observer|'s [=internal observer/next steps=] given |value|.
212211

213-
<div class=note>
214-
<p>Note: No exception can be thrown here because in the case where the
215-
[=Subscriber/internal observer=]'s [=internal observer/next steps=] is just a wrapper
216-
around a script-provided callback, the <a href=#process-observer>process observer</a> steps
217-
take care to wrap these callbacks in logic that, when invoking them, catches any
218-
exceptions, and reports them to the global.</p>
219-
220-
<p>When the [=internal observer/next steps=] is a spec algorithm, those steps take care to
221-
not throw any exceptions outside of itself, to appease this assert.</p>
222-
</div>
212+
[=Assert=]: No <a spec=webidl lt="an exception was thrown">exception was thrown</a>.
213+
214+
<div class=note>
215+
<p>Note: No exception can be thrown here because in the case where the
216+
[=Subscriber/internal observer=]'s [=internal observer/next steps=] is just a wrapper
217+
around a script-provided callback, the <a href=#process-observer>process observer</a>
218+
steps take care to wrap these callbacks in logic that, when invoking them, catches any
219+
exceptions, and reports them to the global.</p>
220+
221+
<p>When the [=internal observer/next steps=] is a spec algorithm, those steps take care
222+
to not throw any exceptions outside of itself, to appease this assert.</p>
223+
</div>
223224
</div>
224225

225226
<div algorithm>
@@ -232,12 +233,13 @@ The <dfn attribute for=Subscriber><code>signal</code></dfn> getter steps are to
232233

233234
1. [=close a subscription|Close=] [=this=].
234235

235-
1. Run [=this=]'s [=Subscriber/internal observer=]'s [=internal observer/error steps=] given
236-
|error|.
236+
1. [=set/For each=] |observer| of [=this=]'s [=Subscriber/internal observers=]:
237+
238+
1. Run |observer|'s [=internal observer/error steps=] given |error|.
237239

238-
[=Assert=]: No <a spec=webidl lt="an exception was thrown">exception was thrown</a>.
240+
[=Assert=]: No <a spec=webidl lt="an exception was thrown">exception was thrown</a>.
239241

240-
Note: See the documentation in {{Subscriber/next()}} for details on why this is true.
242+
Note: See the documentation in {{Subscriber/next()}} for details on why this is true.
241243
</div>
242244

243245
<div algorithm>
@@ -250,11 +252,13 @@ The <dfn attribute for=Subscriber><code>signal</code></dfn> getter steps are to
250252

251253
1. [=close a subscription|Close=] [=this=].
252254

253-
1. Run [=this=]'s [=Subscriber/internal observer=]'s [=internal observer/complete steps=].
255+
1. [=set/For each=] |observer| of [=this=]'s [=Subscriber/internal observers=]:
254256

255-
[=Assert=]: No <a spec=webidl lt="an exception was thrown">exception was thrown</a>.
257+
1. Run |observer|'s [=internal observer/complete steps=].
256258

257-
Note: See the documentation in {{Subscriber/next()}} for details on why this is true.
259+
[=Assert=]: No <a spec=webidl lt="an exception was thrown">exception was thrown</a>.
260+
261+
Note: See the documentation in {{Subscriber/next()}} for details on why this is true.
258262
</div>
259263

260264
<div algorithm>
@@ -407,6 +411,9 @@ interface Observable {
407411
Each {{Observable}} has a <dfn for=Observable>subscribe callback</dfn>, which is a
408412
{{SubscribeCallback}} or a set of steps that take in a {{Subscriber}}.
409413

414+
Each {{Observable}} has a <dfn for=Observable>weak subscriber</dfn>, which is a weak reference to a
415+
{{Subscriber}}-or-null, initially null.
416+
410417
Note: The "union" of these types is to support both {{Observable}}s created by JavaScript (that are
411418
always constructed with a {{SubscribeCallback}}), and natively-constructed {{Observable}} objects
412419
(whose [=Observable/subscribe callback=] could be an arbitrary set of native steps, not a JavaScript
@@ -732,9 +739,38 @@ An <dfn>internal observer</dfn> is a [=struct=] with the following [=struct/item
732739
error algorithm=], or an algorithm that [=invokes=] the provided
733740
{{SubscriptionObserver/error}} [=callback function=].
734741

742+
1. If [=this=]'s [=Observable/weak subscriber=] is not null and [=this=]'s [=Observable/weak
743+
subscriber=]'s [=Subscriber/active=] is true:
744+
745+
1. Let |subscriber| be [=this=]'s [=Observable/weak subscriber=].
746+
747+
1. [=set/Append=] |internal observer| to |subscriber|'s [=Subscriber/internal observers=].
748+
749+
1. If |options|'s {{SubscribeOptions/signal}} [=map/exists=], then:
750+
751+
1. If |options|'s {{SubscribeOptions/signal}} is [=AbortSignal/aborted=], then
752+
[=set/remove=] |internal observer| from |subscriber|'s [=Subscriber/internal
753+
observers=].
754+
755+
1. Otherwise, [=AbortSignal/add|add the following abort algorithm=] to |options|'s
756+
{{SubscribeOptions/signal}}:
757+
758+
1. If |subscriber|'s [=Subscriber/active=] is false, then abort these steps.
759+
760+
1. [=set/Remove=] |internal observer| from |subscriber|'s [=Subscriber/internal
761+
observers=].
762+
763+
1. If |subscriber|'s [=Subscriber/internal observers=] is [=set/empty=], then [=close a
764+
subscription|close=] |subscriber| with |options|'s {{SubscribeOptions/signal}}'s
765+
[=AbortSignal/abort reason=].
766+
767+
1. Return.
768+
735769
1. Let |subscriber| be a [=new=] {{Subscriber}}.
736770

737-
1. Set |subscriber|'s [=Subscriber/internal observer=] to |internal observer|.
771+
1. [=set/Append=] |internal observer| to |subscriber|'s [=Subscriber/internal observers=].
772+
773+
1. Set [=this=]'s [=Observable/weak subscriber=] to |subscriber|.
738774

739775
1. If |options|'s {{SubscribeOptions/signal}} [=map/exists=], then:
740776

@@ -745,8 +781,14 @@ An <dfn>internal observer</dfn> is a [=struct=] with the following [=struct/item
745781
1. Otherwise, [=AbortSignal/add|add the following abort algorithm=] to |options|'s
746782
{{SubscribeOptions/signal}}:
747783

748-
1. [=close a subscription|Close=] |subscriber| with |options|'s
749-
{{SubscribeOptions/signal}} [=AbortSignal/abort reason=].
784+
1. If |subscriber|'s [=Subscriber/active=] is false, then abort these steps.
785+
786+
1. [=set/Remove=] |internal observer| from |subscriber|'s [=Subscriber/internal
787+
observers=].
788+
789+
1. If |subscriber|'s [=Subscriber/internal observers=] is [=set/empty=], then [=close a
790+
subscription|close=] |subscriber| with |options|'s {{SubscribeOptions/signal}}'s
791+
[=AbortSignal/abort reason=].
750792

751793
1. If [=this=]'s [=Observable/subscribe callback=] is a {{SubscribeCallback}}, [=invoke=] it
752794
with |subscriber|.

0 commit comments

Comments
 (0)