Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Syntax Support #207

Open
loreanvictor opened this issue May 20, 2020 · 4 comments
Open

Syntax Support #207

loreanvictor opened this issue May 20, 2020 · 4 comments

Comments

@loreanvictor
Copy link

loreanvictor commented May 20, 2020

The aim of this issue is not expanding the scope of the proposal, but rather contemplation on potential syntax that could follow IF this proposal is accepted.

As it stands, the only integration with JavaScript syntax for Observables would be through implementation of Symbol.asyncIterator (see this and this). This interop would make basic consumption of Observables rather intuitive:

for await (let o of o$) console.log(o);

However, the for await syntax is designed for pulling values from a generator rather than responding to a source emitting values. This for example can cause confusion because we are waiting for the Observable to complete (which might not happen), or leads to a necessity for buffering the values of the Observable so that they can be served when they are pulled by the consumer (which has an overhead and can lead to memory issues).

Generally, while it is enticing to use await to wait for values from sources that push stuff (Promises and Observables), there is a clear distinction between the two: while a Promise only ever pushes one value (so it makes sense to await that one value), an Observable can push many values, which raises the question of consolidating that behavior with a pulling mechanism (e.g. buffering).

An alternative might be an additional syntax support for consumption of Observables, for example through a new keyword:

on (o$) console.log('Got something!');

Which would be translated to:

o$.subscribe(() => console.log('Got something'));

Or with a scoped variable set to emitted value:

on (let o from o$) console.log(o);

Which would be roughly equivalent to:

o$.subscribe(o => console.log(o));

The on keyword could actually happen in a non async context (since it doesn't block the flow).


Inspiring Examples

on (fromEvent(button, 'click'))) {
  const response = await someRequest();
  updateUI();
}
on (let {clientX, clientY} from fromEvent(document, 'mousemove')) {
  console.log(clientX);
}
on (const connection from socket) {
  on (const msg from connection) {
    // handle msg from socket
  } finally {
    // cleanup for connection closing
  }
}

Aborting

Similar to for loops, it can support continue and break keywords:

on (let o from o$) {
  if (o == 3) continue;
  if (o > 10) break;
  console.log(o);
}

Which would roughly translate to:

const _ctrl = new AbortControl();
o$.subscribe(o => {
  if (o == 3) return;
  if (o > 10) {
    _ctrl.abort();
    return;
  }

  console.log(o);
}, undefined, undefined, _ctrl);

Error and Completion Handling

It could also be extended with catch and finally keywords for error/completion handling:

on (let o from o$) {
  /* do stuff */
} catch(err) {
  /* handle the error */
} finally {
  /* handle completion / cleanup */
}

Which would be roughly equivalent to

o$.subscribe(
  o => {
    /* do stuff */
  },
  err => {
    try { /* handle the error */ } 
    finally { /* handle completion / cleanup */ }
  },
  () => {
    /* handle completion / cleanup */
  }
);

It is notable that in this situation the default behavior of .subscribe() would differ from what you would get from the syntax, as finally will also be executed after an error has occurred (to be in sync with behavior of try/catch), which might be a source of confusion.


EDIT: as discussed here, it might be a good idea to make the subscription process asynchronous to make the behavior a bit more predictable.

@benjamingr
Copy link

Are you aware of the (ancient) for..on proposal?

@benjamingr
Copy link

benjamingr commented May 20, 2020

https://github.com/jhusain/compositional-functions

Edit: sorry, meant https://github.com/jhusain/asyncgenerator

@loreanvictor
Copy link
Author

Edit: sorry, meant https://github.com/jhusain/asyncgenerator

Yeah I was reading through the original link and wondering how it related (though definitely interesting on its own).

@loreanvictor
Copy link
Author

loreanvictor commented May 20, 2020

Thanks for sharing. Isn't that proposal actually withdrawn in favor of this proposal?

Edit: still good to know of existence of that proposal, I am going through the discussion on it specifically regarding the for ... on syntax and its caveats now. Specifically this issue seems to apply to what I've suggested here as well, and while my preference would be for the syntax to instantly subscribe since the idea is that you should not care when values are emitted, based on personal experience it seems it is safer to make the subscription asynchronous, as I have for example used a work-around like this a lot:

// do stuff
setImmediate(() => observable.subscribe(...));

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants