loading...
Cover image for Queries for Observables: Crazy & Simple!

Queries for Observables: Crazy & Simple!

kosich profile image Kostia Palchyk ・3 min read

In the previous post we explored API to select events from streams using RegExp-like syntax, e.g.:

const D = fromEvent(element, 'mousedown');
const M = fromEvent(document, 'mousemove');
const U = fromEvent(document, 'mouseup');

exec(
  'DM*U'         // <- regular expression
  , { D, M, U }  // <- streams that will be used
)
  .pipe(…)
  .subscribe(console.log);

In this post we'll review it's functional API counterpart:

query(D, some(M), U) // select some Ms between Ds and Us
  .pipe(…)
  .subscribe(console.log)

But first, we'll recap the thought process. If you're already familiar with the idea β€” hop here

tl;dr: package @ https://github.com/erql/rx-rql πŸ“¦

πŸ’‘ Recap the idea

Imagine we need to implement a drag-n-drop behavior.

For that, we have three event streams: mousemove$, mousedown$, mouseup$. So we want to capture mousemove$ events after mousedown$ emitted and before mouseup$.

Let's draw a marble diagram of the event streams we have:

mousedown$  --o------------
mousemove$  -o-o-o-o-o-o-o-
mouseup$    ------------o--
Every event stream is represented with separate line.
o stands for event on the stream. - is a separator to represent passing time

For better readability let's substitute all the os to respective letters of the streams:

mousedown$  --d------------
mousemove$  -m-m-m-m-m-m-m-
mouseup$    ------------u--
'd' will now stand for mouse-down event, 'm' for mouse-move, 'u' for mouse-up

Now that we have distinct event names, we can simplify our diagram to a single line of events:

events$     -mdm-m-m-m-mum-

Let's remove the time - signs as well, we don't them:

events$      mdmmmmmum

Okay, to rephrase our task in terms of the new diagram: we need to capture the m events between d and u emissions.

πŸ€”

Hmm...

"we need ms between d and u"...

Sounds familiar...

Ah! If that was a string, we could easily do it with a regular expression:

/dm*u/.exec('mdmmmum')

Would give us the needed dmmmu without trailing mouse-move m events...

Right?

If only we had a library to select events from streams with regexes...

πŸš€ Solution

query(D, some(M), U)
  .pipe(…)
  .subscribe(console.log)

Rx-RQL πŸ“¦ package provides following API to make such selections:

  • query(…) β€” root of your selection
  • A β€” select 1 emission from the stream
  • some(A) β€” select 0 to ∞ emissions from A
  • maybe(A) β€” select 0 or 1 emission from A
  • many(n,m)(A) β€” select from n to m emissions from A
  • mute(A) β€” select emission from A & mute it

And you can group them as you like:

  • some(A, some(B), mute(C)) β€” select as many emissions from: select as many Bs as possible between emissions from A and muted C

Here's how to create a simple drag-n-drop behavior using this package:

And here's a Mr. Potato-Head DnD πŸ₯” β€” a more sophisticated example based on this amazing article by @dailydevtips1 ! Thx, Chris πŸ™

πŸ‘‹ Outro

Thank you for reading this article! Stay reactive and have a nice day πŸ™‚

If you enjoyed reading β€” please, indicate that with ❀️ πŸ¦„ πŸ“˜ buttons

And in case you're not yet following me here and on twitter β€” then you've probably missed my recent experiments: Rx + Proxy, Rx Autorun, React + Rx

Now I'd love to hear your thoughts! πŸ‘‚

Discussion

pic
Editor guide
Collapse
yellow1912 profile image
yellow1912

Look very cool. I will try it out.

Collapse
kosich profile image
Kostia Palchyk Author

Thanks! 😊

Be cautious: although it's well covered with tests, it's still very experimental!

If community would find this library useful, I'm thinking it could be extended with:

  • time-based queries, like "listen to A for 5 sec"
  • pipe-able queries, like query(A, many(B).pipe(…), C) that would be applied to particular group output

Anyhow, please ping me back here or on github if you have additional thoughts or ideas. Thx!

Collapse
yellow1912 profile image
yellow1912

I think it would be interesting to create predefined example to solve some common scenarios. Things like Drag and Drop, Hotkeys etc. I can see many possibilities here.