question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Observables v async iterables for subscriptions

See original GitHub issue

There’s a bit of annoying impedance mismatch between working with subscriptions in GraphQL.js and working with subscriptions in Relay.

While GraphQL.js internally uses async iterables for subscriptions, Relay instead uses observables to handle everything.

The semantics between the two are similar, but not identical, and we end up with different-looking code on the receiver side v. on the publisher side.

Additionally, to the extent that there’s a difference, observables are somewhat more powerful, as, analogous to the current value-or-promise handling in resolve, they synchronously respond, while async iterables can’t.

I’m not sure if it’s too late now, but should we consider supporting observables for subscriptions?

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Reactions:1
  • Comments:14 (7 by maintainers)

github_iconTop GitHub Comments

3reactions
taioncommented, Jun 30, 2018

@leebyron @robzhu @Urigo @davidyaha @jedwards1211 cc @jquense

At this point, we’ve done quite a bit of extra work with subscriptions, playing around with async iterables. While async iterables have a number of nice syntax advantages, we’ve discovered them to be quite painful to use and full of pitfalls in this scenario.

Consider the simple case of adapting the messages on a Redis topic to an async iterable, then cleaning up the subscription when killing the iterable. This is actually surprisingly complicated to implement in a generically correct way!

First, we need to keep track of an internal queue and promise to keep track of messages received. Second, we need to do our setup and teardown in very specific ways, because iterable.return() won’t hit the finally block if it’s called before the first message is received on the iterable (https://github.com/apollographql/graphql-subscriptions/issues/143).

In the end, an initial correct implementation looks like this: https://github.com/4Catalyzer/graphql-subscription-server/blob/v0.6.0/src/AsyncUtils.js#L27-L78. This was quite unintuitive to write, and there are a number of fiddly, seemingly-pointless pieces of code that, if removed, could lead to incorrect behavior.

It gets worse, though, because in the simple Redis case, a more correct way to model the subscription type is actually Promise<AsyncIterable>, as making (and receiving the acknowledgement for making) the initial subscription is itself asynchronous. However, because of the noted issue above with iterable cleanup, we need to write yet more unintuitive, fiddly code to get everything to work: https://github.com/4Catalyzer/graphql-subscription-server/pull/18#discussion_r199313379.

The upshot is that, as specified, iterable.return() on an async iterable has semantics that are so deeply unhelpful for this use case that it’s very likely that most implementations of async iterables here are just wrong/buggy.

While there may not be a language-level specification for observables, they’re at least not full of traps for the unwary that could lead to incorrect code. The observable-equivalent version of the code above would be significantly simpler, easier-to-understand, and less bug-prone.

2reactions
leebyroncommented, Dec 12, 2017

I don’t think we should - GraphQL.js subscriptions use async iterables because it’s an impending native syntactic JavaScript feature, while Observable seems unlikely to become native. Support for native syntax is going to be important for the long term sustainability of the feature.

Observables are probably a better fit for client-side observable programming where synchronous responses to observable events is critical (and that is exactly why Relay uses them) but Async Iterables are a better fit for natively asynchronous servers like a GraphQL subscription service.

For cases where the two need to come together, it would be preferable to have simple functions which convert from one to the other.

Read more comments on GitHub >

github_iconTop Results From Across the Web

What are the practical differences between an AsyncIterable ...
You seem to already have answered the question from the title in the body (async iterables are pull-based, observables are push-based). Do you ......
Read more >
Observables compared to other techniques - Angular
Observables differentiate between chaining and subscription. ... Using observables to handle events and asynchronous operations can have the advantage of ...
Read more >
An intro to Observables and how they are different from ...
As I already told you, Observables gave us a new way of handling async requests. The other ways are promises, callbacks, and async/await....
Read more >
Asynchronous iteration • Exploring ES2018 and ES2019
Async iterables are marked via Symbol.asyncIterator . Method next() of an async iterator returns Promises for IteratorResults (vs. IteratorResults directly) ...
Read more >
JavaScript Promises vs. RxJS Observables | by Daniel Weibel
An observable takes the iterable to the asynchronous world. Like an iterable, an observable computes and emits a stream of values. However, ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found