baconjs / bacon.js

Functional reactive programming library for TypeScript and JavaScript
https://baconjs.github.io
MIT License
6.47k stars 330 forks source link

skipDuplicates with Property and take(1) #211

Closed bogus34 closed 11 years ago

bogus34 commented 11 years ago

Test example:


B = require 'baconjs'

b = new B.Bus() p = b.toProperty() p.onValue -> # force property update s = p.skipDuplicates()

b.push 'foo'

s.take(1).onValue console.log, '1' s.take(1).onValue console.log, '2' s.take(1).onValue console.log, '3'


Expected output: 1 foo 2 foo 3 foo

Actual output: 1 foo 3 foo

Why is it behaves so strange?

bogus34 commented 11 years ago

Btw, if I write p = b.toProperty().skipDuplicates() without intermediate variable, it works correctly

philipnilsson commented 11 years ago

I can repro, that's really weird, and kinda unsettling. I might take a look at this. @raimohanska , any ideas?

raimohanska commented 11 years ago

I'm surprised. Did you already make a failing test for this?

raimohanska commented 11 years ago

I found it. The problem is in skipDuplicates implementation where it keeps track of the current value the wrong way. It has a value in a global scope, meaning that when all subscribers are disposed, the value is still there. When the next subscriber comes in, the old current value is applied and the new value is possibly discarded even when it shouldn't.

I've got a fix too, and I'll do some more checks to make sure it works correctly in all cases.

raimohanska commented 11 years ago

Oh, actually my initial fix broke EventStream.skipDuplicates in a very special case. The correct fix is simpler: never drop an Initial event in skipDuplicates. That's it.

raimohanska commented 11 years ago

Fixed in 0.6.3

bogus34 commented 11 years ago

Actually, I've used my own version of skipDuplicates that never drops initial events as a workaround. Sorry not mention it here. But I think this is only a workaround because it leaves some open questions.

1)

p = b.toProperty() s = p.skipDuplicates()

seems to be an equivalent to p = b.toProperty().skipDuplicates() s = p

but it isn't

2) if I rewrote initial code as

b = new B.Bus() p = b.toProperty()

p.onValue -> # force property update

s = p.skipDuplicates() s.subscribe (e) -> console.log '-', e.isInitial(), e.value() # mark 1 b.push 'foo' s.take(1).onValue console.log, '1' s.take(1).onValue console.log, '2' s.take(1).onValue console.log, '3'

I'll see that marked subscribe never gets an initial event but later does and it doesn't loose events anymore.

3) if I add more subscribes like "s.take(1).onValue console.log, 'N'" to first version of test code, I'll see that only second one are loosed. 3, 4, 5 and so on are printed and all theese events are Initial.

All tested with version 0.6.2

raimohanska commented 11 years ago

Did you try 0.6.3? Did it work for you?

bogus34 commented 11 years ago

It works. Just mentioning some points, that looks unclear for me. Thank you.

raimohanska commented 11 years ago

The weird thing there was that

This was caused by the following

After my fix, skipDuplicates always passes through Initial events and should work just fine.

bogus34 commented 11 years ago

Thanks for detailed answer and for this great library!