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

exhaustMap equivalent #157

Closed
midnight-wonderer opened this issue Dec 10, 2016 · 9 comments
Closed

exhaustMap equivalent #157

midnight-wonderer opened this issue Dec 10, 2016 · 9 comments

Comments

@midnight-wonderer
Copy link
Contributor

Hi there
I am new to xstream and I want to use something like exhaustMap operation.
I can not figure out what to do given the available operators (anything like flattenFirst maybe).
What do I do?

@staltz
Copy link
Owner

staltz commented Dec 10, 2016

@MidnightWonderer Hi. In that case you would need to implement it yourself (following the example of flattenConcurrently in extras), publish it as a separate library, and use it with compose.

@xtianjohns
Copy link
Contributor

xtianjohns commented Dec 13, 2016

Do you want to do something like exhaustMap, @MidnightWonderer? Which parts do you want to keep?

The following should be an acceptable operator implementing only exhaust.

import xs, { Stream } from 'xstream';
import flattenSequentially from 'xstream/extra/flattenSequentially';

function exhaust<T>( source ): Stream<T> {
  /* Initialize listening status to false. */
  let listening: boolean = false;
  
  /**
   * Project the source into a stream of streams.
   * Each will have a value or be empty.
   */
  const projected = source.map( element => xs.of( element ) ).map( project );
  
  function project( inner ) {
    /* We're not listening, project the element. */
    if ( !listening ) {
      /* Toggle listening status. */
      listening = true;
  
      /* When that stream ends, set listening status to false. */
      inner.addListener({
        complete: () => {
          listening = false;
        },
      });

      /* Return the inner stream. */
      return inner;
    }
    
    /* We're already listening, emit nothing. */
    return xs.empty();
  }

  /* Flatten the result streams into a single stream. */
  return projected.compose( flattenSequentially );
}

You would use this operator as @staltz says, using compose.

/* Create a source. */
const source = xs.periodic( 100 );

/* Use the operator. */
source.compose( exhaust );

If you also wanted the projection capabilities in exhaustMap, you'd need to modify the operator to support a selector.

function exhaustMap( selector ) {
  return function exhaust<T>( source ): Stream<T> {
    // ...
    /**
     * Project the source into a stream of streams.
     * Each will have a value or be empty.
     */
    const projected = source.map( selector ).map( project );
  
    function project( element ) {
      // ...
    }
    // ...
  }
}

And you can use this operator by providing a selector. Here is a bin.

import delay from 'xstream/extra/delay';
const source = xs.periodic( 1000 );

const selector = () => xs.from( [ 1, 2, 3] ).compose( delay( 1000 ) );

source.compose( exhaustMap( selector ) );

Does this help you implement your feature?

@midnight-wonderer
Copy link
Contributor Author

Hi
Thank you @xtianjohns This is unexpected to me.
I actually just evaluating xstream by curiosity. Not actually need the operation at the moment.
In my project I use switchMap to load data and exhaustMap to save data.
(To prevent unintentional resource creation when people don't patient enough and start resubmitting the data.)

I think it would be nice if xstream also support operator monkey patching like in rxjs 5.
So people will able to customize their own set of operation and open for more code contribution on this repo.

@midnight-wonderer
Copy link
Contributor Author

Ahh sorry I reread the docs.
It seems that xstream don't want to mess with monkey patching.
Even the operators in extra have to be used with compose.

So do you actually accept pull request for more operator into extra?
For example this one by xtianjohns if it comes with proper unit testing.

@staltz
Copy link
Owner

staltz commented Dec 14, 2016

So do you actually accept pull request for more operator into extra?

A better place for exhaust would be as a separate library. One of the goals of xstream is to not have many operators, even if they are extra.

@midnight-wonderer
Copy link
Contributor Author

@staltz thank you for the reply.
I understand that keep thing small allow the community to move fast and better portability.

Then I curious how do you justify which functionality should be in core and which one should be in extra?
And if this is the case whether using compose as much as possible will make the interface more consistent?
I mean why don't we also use compose with core operators?
And people will also able to spot the special cases easier. (e.g. imitate)

@xtianjohns
Copy link
Contributor

It only answers part of your question, @MidnightWonderer, but there is a conversation in #128 about an installation helper to "monkey patch" extra operators. Ideally that helper would be able to "install" operators that are bundled as part of xstream's extras, or your own.

I'm hoping to take a stab at that (#128) soon, but the holidays are getting busier.

@staltz
Copy link
Owner

staltz commented Dec 14, 2016

Then I curious how do you justify which functionality should be in core and which one should be in extra?

Operators in core were the most commonly used RxJS operators in Cycle.js apps. We measured this statistically. We want to keep core around 4kB gzipped/minified. Basically core operators are common for idiomatic Cycle.js and usually you can easily build other operators through some combination of core operators. It's very important that xstream has few operators. It's a feature.

Extra are operators equivalent to RxJS operators commonly used in Cycle.js apps, but not as common as core.

I've never seen a use case for exhaust in Cycle.js. But as I said, this is the type of thing that can perfectly exist in its own package, then you import exhaust and .compose(exhaust).

And if this is the case whether using compose as much as possible will make the interface more consistent?

Ideally we would have |> or function bind syntax in JavaScript. There are proposals for these but they aren't going forward well enough. install is the other idea.

@midnight-wonderer
Copy link
Contributor Author

Thank you both for the clarification.

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

3 participants