Skip to content

Commit a8885ef

Browse files
committed
Spec the finally() operator
1 parent 7b4cb07 commit a8885ef

File tree

1 file changed

+81
-1
lines changed

1 file changed

+81
-1
lines changed

spec.bs

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1105,7 +1105,87 @@ For now, see [https://github.com/wicg/observable#operators](https://github.com/w
11051105
<div algorithm>
11061106
The <dfn for=Observable method><code>finally(|callback|)</code></dfn> method steps are:
11071107

1108-
1. <span class=XXX>TODO: Spec this and use |callback|.</span>
1108+
1. Let |sourceObservable| be [=this=].
1109+
1110+
1. Let |observable| be a [=new=] {{Observable}} whose [=Observable/subscribe callback=] is an
1111+
algorithm that takes a {{Subscriber}} |subscriber| and does the following:
1112+
1113+
1. Let |finally callback steps| be the following steps:
1114+
1115+
1. [=Invoke=] |callback|.
1116+
1117+
If <a spec=webidl lt="an exception was thrown">an exception |E| was thrown</a>, then run
1118+
|subscriber|'s {{Subscriber/error()}} method, given |E|, and abort these steps.
1119+
1120+
1. [=AbortSignal/add|Add the algorithm=] |finally callback steps| to |subscriber|'s
1121+
[=Subscriber/signal=].
1122+
1123+
Note: This is necessary to ensure |callback| gets invoked on *consumer-initiated*
1124+
unsubscription. In that case, |subscriber|'s [=Subscriber/signal=] gets
1125+
[=AbortSignal/signal abort|aborted=], and neither the |sourceObserver|'s
1126+
[=internal observer/error steps=] nor [=internal observer/complete steps=] are invoked.
1127+
1128+
1. Let |sourceObserver| be a new [=internal observer=], initialized as follows:
1129+
1130+
: [=internal observer/next steps=]
1131+
:: Run |subscriber|'s {{Subscriber/next()}} method, given the passed in <var
1132+
ignore>value</var>.
1133+
1134+
: [=internal observer/error steps=]
1135+
:: 1. Run the |finally callback steps|.
1136+
1137+
<div class=example id=manual-finally-callback-steps>
1138+
<p>This "manual" invocation of |finally callback steps| is necessary to ensure
1139+
that |callback| is invoked on producer-initiated unsubscription. Without this,
1140+
we'd simply delegate to {{Subscriber/error()}} below, which first [=close a
1141+
subscription|closes=] the subscription, *and then* [=AbortSignal/signal
1142+
abort|aborts=] |subscriber|'s [=Subscriber/signal=].</p>
1143+
1144+
<p>That means when |finally callback steps| eventually runs as a result of
1145+
abortion, |subscriber| would already be [=Subscriber/active|inactive=]. So if
1146+
|callback| throws an error during, it would never be plumbed through to
1147+
{{Subscriber/error()}} (that method is a no-op once
1148+
[=Subscriber/active|inactive=]). See the following example which exercises this
1149+
case exactly:</p>
1150+
1151+
<pre highlight=js>
1152+
const controller = new AbortController();
1153+
const observable = new Observable(subscriber =&gt; {
1154+
subscriber.complete();
1155+
});
1156+
1157+
observable
1158+
.finally(() =&gt; {
1159+
throw new Error('finally error');
1160+
})
1161+
.subscribe({
1162+
error: e =&gt; console.log('erorr passed through'),
1163+
}, {signal: controller.signal});
1164+
1165+
controller.abort(); // Logs 'error passed through'.
1166+
</pre>
1167+
</div>
1168+
1169+
1. Run |subscriber|'s {{Subscriber/error()}} method, given the passed in <var
1170+
ignore>error</var>.
1171+
1172+
Note: The |finally callback steps| possibly calls |subscriber|'s
1173+
{{Subscriber/error()}} method first, if |callback| throws an error. In that case, it
1174+
is still safe to call it again unconditionally, because the subscription will
1175+
already be closed, making the call a no-op.
1176+
1177+
: [=internal observer/complete steps=]
1178+
:: 1. Run the |finally callback steps|.
1179+
1180+
1. Run |subscriber|'s {{Subscriber/complete()}} method.
1181+
1182+
1. Let |options| be a new {{SubscribeOptions}} whose {{SubscribeOptions/signal}} is
1183+
|subscriber|'s [=Subscriber/signal=].
1184+
1185+
1. <a for=Observable lt="subscribe to an Observable">Subscribe</a> to |sourceObservable|
1186+
given |sourceObserver| and |options|.
1187+
1188+
1. Return |observable|.
11091189
</div>
11101190

11111191

0 commit comments

Comments
 (0)