Sorry, make that LGTM2
On Wed, Mar 5, 2025 at 1:00 PM Chris Harrelson <chris...@chromium.org>
wrote:
Thanks Dominic!
LGTM1
On Wed, Mar 5, 2025 at 12:57 PM Dominic Farolino
<d...@chromium.org> wrote:
Thanks for the summary Jake. So from the perspective of API
OWNERS, I don't believe anything is blocking here. Folks agree
that the ref-counted producer design is the right way,
consistent with the developer feedback, and while you can hold
the API in a way that appears surprising, (1) there are more
surprises/quirks with the non-ref-counted approach, and (2)
our design is consistent with the ways developers use
Observables in the wild, mitigating the consequences of any
surprises—I believe we're making the right trade-off.
Given that, I believe we can proceed with the review.
On Fri, Feb 28, 2025 at 12:32 PM Jake Archibald
<jaffathec...@gmail.com> wrote:
By missing the end, I mean this:
const ob = new Observable((subscriber) => {
subscriber.next(1);
setTimeout(() => {
subscriber.next(2);
subscriber.complete();
}, 1000);
});
ob.toArray().then((vals) => {
// You're first, so you get things from the start.
console.log(vals); // [1, 2]
});
ob.toArray().then((vals) => {
// You missed the start, so you get the remaining values.
// I'd describe the model here as: too late, you miss out!
console.log(vals); // [2]
});
setTimeout(() => {
ob.toArray().then((vals) => {
// You missed the end, so we restart.
// I'd describe the model here as: we'll fix it so you
don't miss out
console.log(vals); // [1, 2]
});
}, 1500);
The bit where it sometimes restarts the thing so you don't
miss out, and sometimes doesn't, felt unusual to me. To be
clear, I think the ref-counting approach is right, but it
would have felt more consistent if the final log was [],
since the thing had already completed.
The other case I found inconsistent is:
const ob = Observable.from([1, 2, 3]);
ob.toArray().then((vals) => {
console.log(vals); // [1, 2, 3]
});
ob.toArray().then((vals) => {
console.log(vals); // [1, 2, 3]
});
vs
const ob = Observable.from([1, 2, 3].values());
ob.toArray().then((vals) => {
console.log(vals); // [1, 2, 3]
});
ob.toArray().then((vals) => {
console.log(vals); // []
});
I had a meeting with Dominic and I now understand why it
happens. It still seems unusual, but given that this
hasn't come up for anyone else looking at the API (people
who have way more experience with observables than I do),
I guess it's just that I'm unfamiliar with these patterns.
It's certainly something I'd call out in developer
documentation for others coming to this fresh.
Thanks all!
Jake.
On Fri, 28 Feb 2025 at 15:32, Dominic Farolino
<d...@chromium.org> wrote:
Responding to Jake:
With the example in the codepen, as the holder of
the observable, I don't think I have a way of
knowing if I'm getting the start, or something in
the middle. Isn't that a bit odd?
Hmm, I see how it can feel a little intuitive, but I
think this tradeoff is _less unintuitive_ than it was
without ref-counted producers, where Observable
doesn't really represent anything related to the
subscription, causing the footguns that led to us
pursuing this path in the first place. Regarding:
If it's ok to miss the start, why isn't it ok to
miss the end?
I don't think it is OK to miss the end, and I don't
quite think our proposal makes this possible? If you
subscribe half-way through, you will still get
`complete()` notifications so you know that the stream
has ended. The closest example of "missing the end" I
can think of would be the one you mentioned over
X/Twitter, which is if you subscribe to an async
iterator (not iterable that can be restarted), and you
exhaust the iterator, what do subsequent subscriptions
do after the iterator is exhausted?
* async function* asyncNumbers() {
yield* [1,2,3,4];
}
const ob = Observable.from(asyncNumbers());
await ob.toArray().then(result => console.log('one',
result));
ob.subscribe({
next: v => console.log('second subscription: ', v),
complete: () => console.log('complete'),
})*
By the time the second subscription rolls around, the
iterator has been exhausted. But you still don't "miss
the end" since the `complete()` handler fires.
Hopefully that makes sense. Either way, it's entirely
possible this thread isn't the best place to hash all
of this out :)
On Fri, Feb 28, 2025 at 10:20 AM Dominic Farolino
<d...@chromium.org> wrote:
I was told that it would be good to clarify
something here from the original email sent here,
about *TC39's engagement* and *WebKit's standards
position*.
/WebKit/: Positive
(https://github.com/WebKit/standards-positions/issues/292)
I marked WebKit's standards position as positive
since Anne had mentioned WebKit folks were
supportive and he recommended marking the issue as
`position: support`
<https://github.com/WebKit/standards-positions/issues/292#issuecomment-2520739850>.
However, he has since walked it back since the
proposal has not been formally presented to TC39.
Such a presentation is abnormal for web APIs not
developing /within/ TC39, however is still a
reasonable idea that I am happy to do. (For what
it's worth, I tried to present this proposal to
TC39 at the Tokyo virtual meeting in October 2024
after TPAC last year, and unfortunately after
staying up late to do, so I got bumped from the
agenda last minute because other items went over
time).
Regarding my comment on *TC39 engagement*, I wrote:
We've gotten good design feedback from TC39
members on many issues which we have
implemented accordingly.
This is true—various ECMAScript editors have
engaged with us on substantial design issues.
However, since we have not formally presented to
TC39, I was made aware that this kind of
engagement might not count as proper _TC39
engagement_. So I wanted to call out here that we
have not yet sought or received any kind of formal
"sign-off" by ECMAScript editors on our proposal.
On Thu, Feb 27, 2025 at 1:00 AM Domenic Denicola
<dome...@chromium.org> wrote:
I looked into
<https://github.com/WICG/observable/issues/177#issuecomment-2686242878> the
SuppressedError proposal a bit more. I'm now
about 90% convinced SuppressedError does not
need to be used. (Or if there is a case for
it, it's in extreme edge cases that we could
address after shipping.)
Given how complete every other aspect of this
Intent is, LGTM1, conditional on Dominic
agreeing with my reasoning that we don't want
to use SuppressedError for most callbacks. If
I misunderstood, then we should delay until
that gets straightened out.
On Thu, Feb 27, 2025 at 5:53 AM Jake Archibald
<jaffathec...@gmail.com> wrote:
On Wed, 26 Feb 2025 at 20:39, Dominic
Farolino <d...@chromium.org> wrote:
https://codepen.io/jaffathecake/pen/raNWMmK?editors=0012 -
it seems inconsistent that two of
the calls to ob.map create a new
subscriber, whereas the other
picks up the observable half way
through.
Right, the idea that a subscription
doesn't have side effects if an
existing subscription is in-flight was
essentially the outcome of
https://github.com/WICG/observable/issues/170
&
https://github.com/WICG/observable/issues/178.
The alternative, where
producer:consumer are 1:1, made it
easy to write performance foot-guns
where what you actually want is to tap
into an existing stream of values
without paying the cost of setting it
up each time if it already exists.
Many userland Observables inevitably
get `share()` slapped on them
somewhere in the chain to alleviate
this, but the inconsistency made it
hard to judge whether your
subscription would have side-effects
or not. We also saw a lot of
Observable learning material
was taking pains to caveat
<https://ronnieschaniel.com/rxjs/rxjs-mastery-hot-vs-cold-observables/#:~:text=Why%20do%20we%20need%20cold%20and%20hot%20Observables%20in%20RxJS%3F>
right
away, this unintuitive idea that
the Observable type itself doesn't
represent anything but a stateless
subscription vendor. Now it basically
represents the producer, and I think
that matches peoples' mental models
<https://github.com/WICG/observable/issues/178#issuecomment-2480525113>.
I guess the rule is: A new
subscriber is created if the
observer has closed, but isn't
this really inconsistent?
I think it is consistent though, no?
It's true that it's neither "only one
call to the subscriber" nor "each call
to the initial observable initiates a
new subscription". But it is similar
to what you wrote above: a subscriber
is invoked/spun up if its subscription
is closed (not observer). The pay-off
is that you know you're never going to
have "extra" side effects when
subscribing. At most you will spin up
a single producer (which you're OK
with since you're subscribing), and at
best you will listen in on an existing
one.
I think where it gets confusing is when
the observable has a beginning and an end.
It's fine for event targets, because they
don't have that.
For event target observables it's
'interested' (add the listener) and
'distinerested' (remove the listener).
Whereas the underlying events are still
continuing.
With the example in the codepen, as the
holder of the observable, I don't think I
have a way of knowing if I'm getting the
start, or something in the middle. Isn't
that a bit odd? If it's ok to miss the
start, why isn't it ok to miss the end?
Again it might be because I'm not used to
the patterns, and they're well understood
elsewhere.
If you need a new subscriber to be
created on each subscription, you'll
need to basically take a closure over
the Observable-vending API and call
each `subscribe()` on it, which I hope
is not too burdensome.
On Wed, Feb 26, 2025 at 2:55 PM Jake
Archibald <jaffathec...@gmail.com> wrote:
I'm struggling a little to get my
head around how this works.
https://codepen.io/jaffathecake/pen/raNWMmK?editors=0012
- it seems inconsistent that two
of the calls to ob.map create a
new subscriber, whereas the other
picks up the observable half way
through.
If I put the .complete call in a
setTimeout, then there's only one
subscriber created.
I guess the rule is: A new
subscriber is created if the
observer has closed, but isn't
this really inconsistent?
I'd expect it to be one way or the
other. As in:
There's only one call to the
subscriber.
Or
Each call to the initial
observable is a new subscription.
The way it's neither one or the
other is confusing to me. But
maybe that's totally normal for
folks who are used to observables?
On Friday, 21 February 2025 at
21:25:05 UTC Chromestatus wrote:
Contact emails
d...@chromium.org
Explainer
https://github.com/WICG/observable
Specification
https://wicg.github.io/observable
Summary
Observables are a popular
reactive-programming paradigm
to handle an asynchronous
stream of push-based events.
They can be thought of as
Promises but for multiple
events, and aim to do what
Promises did for
callbacks/nesting. That is,
they allow ergonomic event
handling by providing an
Observable object that
represents the asynchronous
flow of events. You can
"subscribe" to this object to
receive events as they come
in, and call any of its
operators/combinators to
declaratively describe the
flow of transformations
through which events go. This
is in contrast with the
imperative version, which
often requires complicated
nesting with things like
`addEventListener()`. For more
on this, see the examples in
the explainer. The big selling
point for native Observables
is their integration with
EventTarget — its proposed
`when()` method that returns
an Observable which is a
"better" `addEventListener()`.
See
https://github.com/WICG/observable
and
https://twitter.com/domfarolino/status/1684921351004430336.
See the spec
https://wicg.github.io/observable/
and the design doc:
https://docs.google.com/document/d/1NEobxgiQO-fTSocxJBqcOOOVZRmXcTFg9Iqrhebb7bg/edit.
Blink component
Blink>DOM
<https://issues.chromium.org/issues?q=customfield1222907:%22Blink%3EDOM%22>
TAG review
https://github.com/w3ctag/design-reviews/issues/902
TAG review status
Issues addressed
Risks
Interoperability and
Compatibility
Initially we proposed adding
the `.on()` method to
EventTarget, which was found
to conflict with userland
versions of the same method.
The conflict was found to be
too significant to justify
shipping our native version of
this API (see
https://github.com/WICG/observable/issues/39)
so we renamed it to `.when()`
and we strongly believe this
resolves any naming collision
issues after searching through
public libraries and
performing developer outreach
on X. See the discussion on
that issue.
/Gecko/: No signal
(https://github.com/mozilla/standards-positions/issues/945)
/WebKit/: Positive
(https://github.com/WebKit/standards-positions/issues/292)
/Web developers/: Strongly
positive
(https://twitter.com/domfarolino/status/1684921351004430336)
Also see
https://foolip.github.io/spec-reactions/
and the developer interest in
the original WHATWG DOM issue.
/Other signals/: We've gotten
good design feedback from TC39
members on many issues which
we have implemented
accordingly. This has led to
positive feedback from
Node.js, and luke-warm
non-negative feedback from
WinterCG. See
https://github.com/WICG/observable/issues/93;
specifically
https://github.com/nodejs/standards-positions/issues/1
&
https://github.com/WICG/observable/issues/30
for Node, and
https://github.com/wintercg/proposal-minimum-common-api/issues/72
for WinterCG.
WebView application risks
Does this intent deprecate or
change behavior of existing
APIs, such that it has
potentially high risk for
Android WebView-based
applications?
None
Debuggability
The developer experience of
Observables might benefit from
Observable-specific DevTools
tracking of events and streams
(see
https://github.com/WICG/observable/issues/55).
It is possible that the
existing DevTools work that
assists asynchronous task
tracking and callstack tagging
may be sufficient though. At
the moment, however, our
effort is focused on the
platform implementation of
Observables.
Will this feature be
supported on all six
Blink platforms
(Windows, Mac, Linux,
ChromeOS, Android, and
Android WebView)?
Yes
Is this feature fully
tested by
web-platform-tests
<https://chromium.googlesource.com/chromium/src/+/main/docs/testing/web_platform_tests.md>?
Yes
See
https://wpt.fyi/results/dom/observable/tentative.
Flag name on about://flags
observable-api
Finch feature name
ObservableAPI
Requires code in //chrome?
False
Tracking bug
https://bugs.chromium.org/p/chromium/issues/detail?id=1485981
Estimated milestones
Shipping on desktop 135
Anticipated spec changes
Open questions about a feature
may be a source of future web
compat or interop issues.
Please list open issues (e.g.
links to known github issues
in the project for the feature
specification) whose
resolution may introduce web
compat/interop risk (e.g.,
changing to naming or
structure of the API in a
non-backward-compatible way).
Issues with the "possible
future enhancement" label [1]
track possible changes to the
feature that may come after we
ship the initial API. One
issue
(https://github.com/WICG/observable/issues/200)
is identified to have behavior
changes that theoretically
pose a compat risk, but only
for developers that subclass
the API. The behavior change
proposed puts the
implementation more inline
with what subclass users want:
the operators that return
native Observable objects
would instead return objects
of `this.constructor` type, as
to return instances of the
subclass that the operators
are called on. This is how JS
built-ins like `Array` work,
however, no other web platform
feature works like this and it
likely requires non-trivial
Web IDL support. [1]:
https://github.com/WICG/observable/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22possible%20future%20enhancement%22
Link to entry on the
Chrome Platform Status
https://chromestatus.com/feature/5154593776599040?gate=5141110901178368
Links to previous
Intent discussions
Intent to Prototype:
https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CAP-uykBH1%3DUoLN6%3DBRSEZE%2B1iUq6UdcTpo3qtTQ5T%3DSRxwnu5Q%40mail.gmail.com
This intent message was
generated by Chrome Platform
Status
<https://chromestatus.com>.
--
You received this message because you are
subscribed to the Google Groups
"blink-dev" group.
To unsubscribe from this group and stop
receiving emails from it, send an email to
blink-dev+unsubscr...@chromium.org.
To view this discussion visit
https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CAJ5xic-3d9ziBOmqHYiSGPxLmDzhu19vfbQHffqJSkprFcE%2Btg%40mail.gmail.com
<https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CAJ5xic-3d9ziBOmqHYiSGPxLmDzhu19vfbQHffqJSkprFcE%2Btg%40mail.gmail.com?utm_medium=email&utm_source=footer>.
--
You received this message because you are subscribed to the
Google Groups "blink-dev" group.
To unsubscribe from this group and stop receiving emails from
it, send an email to blink-dev+unsubscr...@chromium.org.
To view this discussion visit
https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CAP-uykDqj_RgJ8xkPvkgxeGqVbj9vHbipMuzMjR35fH%3DpY-NJw%40mail.gmail.com
<https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CAP-uykDqj_RgJ8xkPvkgxeGqVbj9vHbipMuzMjR35fH%3DpY-NJw%40mail.gmail.com?utm_medium=email&utm_source=footer>.
--
You received this message because you are subscribed to the Google
Groups "blink-dev" group.
To unsubscribe from this group and stop receiving emails from it, send
an email to blink-dev+unsubscr...@chromium.org.
To view this discussion visit
https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CAOMQ%2Bw_U3Wa45QJKUB-ned8EVoHCjKJgC5ECNjLsR7Or%3D_kv%3DQ%40mail.gmail.com
<https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CAOMQ%2Bw_U3Wa45QJKUB-ned8EVoHCjKJgC5ECNjLsR7Or%3D_kv%3DQ%40mail.gmail.com?utm_medium=email&utm_source=footer>.