Closed dmitriz closed 7 years ago
Hello @dmitriz thanks for the issue. In most.js, as well as other stream libraries, there is a general property that streams are lazy or in other words do nothing until they are activated. In most.js there are a few operators to do so, but the most common are observe
and drain
. I think observe
is what you're looking for here.
// with observe
most.from([1, 2]).observe(x => console.log(x))
// the way observe(f) is *actually* implemented using tap(f) + .drain()
most.from([1, 2]).tap(x => console.log(x)).drain()
Hope this helps!
@dmitriz You can check out the documentation for consuming streams here: https://github.com/cujojs/most/blob/master/docs/api.md#consuming-streams
Thank you @TylorS for the explanation and @davidchase for the reference.
It feels a bit confusing as this behaviour differs form other stream libraries such as flyd
or mithril/stream
. I knew that was how observable behave but didn't expect it from streams.
Any use cases when this might be beneficial over the always-active approach by flyd
?
It would really help (at least to me) to have some more detailed explanation of what "consuming" precisely means and which methods do consume and which don't. Right now the section on "Consuming streams" starts with the reduce
method that does not say anything about the consumption. The other methods descriptions only say "Start consuming events from stream" but don't explain what it means...
Also this is not clear to me from https://github.com/cujojs/most/blob/master/docs/api.md#observe: "The returned promise will fulfill after all the events have been consumed"
For instance, if our stream keeps running indefinitely, how can all the events be consumed? What if more events will keep coming in? Shall we still observe them?
What I am really looking for is the equivalent of the map
from flyd
, where the passed function simply gets called with every emitted value. I understand that observe
does not provide this as it returns a Promise, where I really want to return another dependent Stream. Is it possible with most
?
Sorry for the naive questions ;)
@dmitriz As @TylorS explained, observe (f, stream)
is really drain(tap(f, stream)
. f
is your provided function that will perform a side effect on each item in the stream. Any return value of f
is ignored. tab
will then return a new stream with the same items as in the originally provided stream. The new stream is passed to drain
, which returns a Promise
. This Promise
either fulfills when the stream ends without an error or is rejected if the stream ends with an error. Take note that the events of the stream are time-ordered.
Thus, if you have an "infinite" stream, e.g., a source that emits an event periodically, the provided function f
will fire on every event, performing whatever side effect specified. Logically, the returned Promise
will neither resolve nor reject (granted no errors occurred). After all, the stream is "infinite". If the stream was finite (it ends), the code will then choose the path of either resolved or rejected Promise
, if you so choose. As you see, the events are still consumed and passed to f
.
You can easily map
over the stream as in flyd, but you still need to consume the stream:
const stream = from([1, 2, 3])
const transformedStream = map(add2, stream)
function add2(value) { return value + 2 }
observe(console.log, transformedStream) // 3, 4, 5
Hope this helps (and that I didn’t provide any incorrect information in my explanation).
@Frikki Thank for your explanation.
tap
will then return a new stream with the same items as in the originally provided stream.
Possibly that is my problem. Can I do it without creating a new stream? Like simply attach a passive observer but keep the stream unchanged and going?
That is what flyd
streams do, and then you can pipe values into them via the setter syntax stream(value)
, which is something I was looking for in most
but was unable to find. How can I pipe a value into existing active stream?
Logically, the returned Promise will neither resolve nor reject (granted no errors occurred). After all, the stream is "infinite".
That makes me confused. Does the stream not get forked into finite one, before being passed to the Promise? And is there any benefit of this approach over simply attaching a passive observer that will return the same value as the promise but can also be reused for the future values? That is, what is the benefit of transferring into promise instead of leaving it as stream?
Sorry if those questions are naive, I'm just trying to understand how the things work.
To give perhaps some motivation for my question, I have been looking at this very nice example. The approach is based on reaching out for a selector to read from the input stream. However, as e.g. noted in http://www.christianalfoni.com/articles/2016_04_06_CycleJS-driven-by-state :
It is indeed very weird that our view says nothing about how it changes state and our state change logic just has some random class names it listens to. This selector also has some challenges thinking scalability. Imagine an application with hundreds of elements and you depend on classnames to find your elements. It is very fragile. Maybe we can do something about that.
I have to agree with it, as there can be potentially hundreds of selectors but only few action streams to subscribe. So it feels a more scalable approach to invert the control by giving to the vnode the right to dispatch actions into the action stream, instead of the stream itself reaching out for the selectors in the wild. This is also very close to the Redux architecture.
But for this to work, I need the ability to dispatch values into active streams, which should be passed to the subscribers without any extra work. Now I am puzzled how to achieve this with most
as I don't see how to activate my stream and how to dispatch into it thereafter.
Sorry if those questions are naive, I'm just trying to understand how the things work.
@dmitriz your questions are not naive, we are all just trying to learn.
Now I am puzzled how to achieve this with most as I don't see how to activate my stream and how to dispatch into it thereafter.
Im not 100% sure that this is what you want/need but check out our community package Most Subject. This should allow you to create a stream and then push values on it akin to what i believe flyd allows you to do.
let me know if this helps at all 😄
@davidchase Many thanks, yes, that is exactly what I was looking for!
That means, I can enable the un
library to be powered by the mostjs
stream creator factory, and expose the streams for both actions and states from each UI uncomponent. That will allow people already using most
to control their un
-components with the same stream api.
Would there be interest in such functionality?
@dmitriz glad that worked for you 👍
That means, I can enable the un library
The library looks interesting i will have to take a closer look in a bit
Would there be interest in such functionality?
What is the functionality you are proposing ?
@davidchase
The library looks interesting i will have to take a closer look in a bit
Thank you 😄
I've just expanded the intro and added more examples. Any feedback is welcome!
Would there be interest in such functionality?
What is the functionality you are proposing ?
I have added more details here: https://github.com/dmitriz/un#full-reactive-control-of-your-uncomponents
Let me know if that makes sense or needs to make the point more clear.
@dmitriz The original issue here seems to be resolved (observing the stream activates it), and it seems like @davidchase's suggestion of looking at most-subject was helpful for your subsequent questions. So, I'm closing, but please re-open or create a new issue if there's more we need to discuss.
Cheers!
Summary
The following code does not seem to show the stream:
Expected result
Expected:
1
followed by2
in the consoleActual Result
In the Node REPL:
Versions
node@6.9.2
Steps to reproduce
Node REPL
Code to reproduce