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.

Double-sent requests

See original GitHub issue

From @staltz on July 17, 2015 9:2

function main(responses) {
  console.log('This only gets called once.')
  const url = '/ontologies/acl.ttl';
  var request$ = Cycle.Rx.Observable.just(url);

  var vtree$ = responses.http
    .do(r => console.log('this gets called twice', r))
    .mergeAll()
    .do(r => console.log('this gets called once', r))
    .map(res => h('p', res.text.substr(0, 100) + '...'));

  return {
    dom: vtree$,
    http: request$
  };
}

let [requests, responses] = Cycle.run(main, {
  dom: makeDOMDriver('#app-container'),
  http: makeHTTPDriver(),
  db: makeLocalDbDriver(),
});

Copied from original issue: cyclejs/cycle-http-driver#9

Issue Analytics

  • State:closed
  • Created 8 years ago
  • Comments:29 (22 by maintainers)

github_iconTop GitHub Comments

11reactions
ivan-kleshnincommented, Mar 15, 2016

I did some small POC with Cycle some time ago. I remember I had some problem using .share on responses from HTTPDriver and had to use .shareReplay(1) instead. If you would kill me I can’t remember how the problem has manifested, so I apologize it is really low value input.

@vladap shareReplay(1) is “required” for streams with scan() otherwise they don’t broadcast a “current” state on subscribe. Usually it leads to initial state being “missed”. So my rule of thumb is to add share() to every stream you expose (outside function / module) and to add shareReplay(1) to every stateful stream.

The other thing with scan() is that such streams are moving away from event streams to data streams. For event streams timing is crucial so --1--1--> may very well differ from --1---->.

For data streams they are kinda the same so we can discard adjacent events carrying the same data. Hence avoiding excessive recalculations bound to a state change. So you’ll probably want to distinctUntilChanged() streams with scan() most of the time.

I’m actually playing with a store abstraction:

// always :: a -> b -> a
let always = curry((x, y) => x); // constant function

// scanFn :: s -> (s -> s) -> s
let scanFn = curry((state, updateFn) => {
  if (typeof updateFn != "function" || updateFn.length != 1) {
    throw Error("updateFn must be a function of arity 1")
  } else {
    return updateFn(state);
  }
});

// store :: s -> Observable (s -> s)
let store = curry((seed, update$) => {
  return update$
    .startWith(seed)
    .scan(scanFn) 
    .shareReplay(1)
    .distinctUntilChanged();
});

// do not use `scan(seed, scanFn)` option because you *need* to cast seed state
// and RxJS does that only with `startWith()` (unlike other FRP libs)

Which works great with currying. You just need to feed a “store” unary functions (from state to state or s -> s).

let update = { // a bucket of update channels
  users$: new Subject(),
};

let state = { // a bucket of state reducers
  users$: store([], update.users$), 
}; 

// RxJS v5
update.users$.next(append({id: "1", name: "John Doe"})); // push first user
update.users$.next(append({id: "2", name: "Jane Doe"})); // push second user
update.users$.next(always({}));                          // reset users
9reactions
ivan-kleshnincommented, Mar 17, 2016

I apologize for spamming this thread 😥 Here is one final post.

Has been share() and shareReplay(1) somehow reworked in RxJs 5 for better good or is it quite same?

It’s getting better. For example, they renamed replay to publishReplay which is a good clarification.

I’ll try to explain. Everything is quite simple once you get it 😄


  1. RxJS has cold Observable and hot Subject primitives. In short: cold is deterministic streams while hot is for non-deterministic ones. That’s a long story so I’ll just say I would prefer having Hot by default. Requirement of manual share() is not satisfying and error-prone.

  2. To make an Observable behave (like) hot RxJS fake it using Subject under the cover. Because this is the simplest implementation.

  3. RxJS exposes a multicast() operator which does what desribed in 2. But it expects an exact Subject type to work. And there is more than one…

  4. So RxJS predefines two flavors of multicast. Just for your convenience.

    publish()       // multicast() with Subject() (regular subject)
    publishReplay() // multicast() with ReplaySubject() (replay subject)
    

    If you look at the RxJS sources – those above are almost oneliners (extra code is for polymorphism and other stuff irrelevant for explanation).

  5. There is one more step. Hot Observables need to be run manually (almost by definition). So you call connect() on them to start event flow.

    But often you want to avoid this manual book-keeping and fire a flow automatically once you have a first subscriber.

    So RxJs implements a refCount() operator which does that for you.

    These are also oneliners. One for each code branch above.

    share()       // publish() with refCount()
    shareReplay() // publishReplay() with refCount()
    

To recap

// Generic Cold -> Hot
.multicast() // requires Subject type and connect()

// Basic Cold -> Hot 
.publish() // multicast() + regular Subject(), requires connect()

// Basic Cold -> Hot with auto-connect
.share() // publish() + refCount()

// Replaying Cold -> Hot
.publishReplay() // multicast() + ReplaySubject(), requires connect()

// Replaying Cold -> Hot with auto-connect
.shareReplay() // publishReplay() + refCount()

Of course all above is oversimplified but I hope you’ve got the point 😉 My explanation still misses two points:

  1. Why Hot is Multicast and Cold is Unicast? Or vice versa…
  2. What is actually “replaying”?

But (as I said), I’m going to write an article and I don’t have a space to cover all this here.

Read more comments on GitHub >

github_iconTop Results From Across the Web

HTTP requests being executed multiple times #262 - GitHub
I'm trying to build a simple login form where, in the happy path, you type your email and password, click login, a network...
Read more >
How should I word this? I double-sent an order because of the post ...
I double-sent an order because of the post office. So I hope this is allowed. I sell on Etsy, 3d printed stuff ......
Read more >
154484 – [patch] request for new functionality. jail zfs dataset on jail ...
No way to define zfs dataset to use it whithin jail on jail start up by /etc/rc.d/jail Fix: I've written simple patch to...
Read more >
Why did the person I sent a message to get duplicate ...
Enter the phone number which the messages were sent to and press "Make Request". An XML or JSON file listing the messages sent...
Read more >
The Sportula: Microgrants for Classics Students on Twitter: "1 ...
1 grantee we double-sent $; they told us and returned it. ... What gets me about so many of these requests is how...
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