Streams really are just the asynchronous version of `Iterable`.
An Iterable can be backed by multiple different classes (Set, List, sync*,
...). As a user of the Iterable you generally don't need to know. The only
thing you know is that you can iterate over the elements.
The same is true for Streams. A Stream is an asynchronous Iterable: you can
listen to it and get events. Whether the source is buffered, broadcast, ...
is something you generally don't need to be concerned about, just what
events it promises to give you, and what you do with them.
In C#, file operations are modelled on top of `Enumerable` (our
`Iterable`). So having different kinds of Iterables isn't unheard of.
See here for the most common answer to "how to read lines from stdin":
public static IEnumerable<string> ReadLines()
{
string line;
while(null != (line = Console.ReadLine()))
yield return line;
}
This is an enumerable that won't behave the same way as a List does. If
it's iterated twice, it will behave surprisingly and it can't be restarted
from scratch. It's still a valid IEnumerable satisfying all the
requirements of such.
To make this even clearer, we distinguish between the "Stream" and the
"Stream source" (aka "event source", "event emitter"). Streams can be made
in different ways, but most are either async* functions or by a
`StreamController` (and internally, async* functions use a StreamController
too). The decisions about which events are emitted when are handled by the
code the feeds the events into the stream. The users of Streams should only
see a "Stream" (similar to Iterable) and not care about the details of how
it's built.
Post by tatumizer-v0.2For example, a new File("foo.txt") may be implemented as a multicast
stream, where each listener just opens the file again.
Not sure I understand how it all works. Some streams pretend they are
completely buffered THEMSELVES? Regardless of listeners?
I think you are thinking of streams as its sequence of events. That's why
you think that it's *buffering*, because the events are the primary focus.
If you have a stream corresponding to a file, you can let each listener
independently read that file. If the file changes between subscriptions,
the newer ones see the new content.
The only difference between that and using a single-subscription stream is
that you need to create two single-subscription streams to read the file
twice, but you can listen twice to the multi-subscription stream. The
actual events are exactly the same in the two cases.
Streams are not their events because the same stream can provide different
events depending on when it's listened to, and how it's designed to behave
when listener to more than once. That's a design choice for the code that
creates the stream, not an inherent property of the stream itself.
You could have a stream like `randomIntegers` which provide a sequence of
random integers in the range 0..255, one every second. You can listen more
than once, each listener gets their own uncorrelated random numbers.
Or a stream of unique ID numbers:
var _id = 0;
var ids = new Stream<int>.multicast((c) {
var t;
run() {
t = new Timer.periodic(const Duration(seconds: 1), (_) {
c.addSync(_id++);
});
}
c.onCancel = c.onPause = () {
t.cancel();
};
(c.onResume = run)();
});
This stream can be listened to multiple times. Each individual listener
gets a sequence of unique numbers.
The numbers are highly correlated, but the streams have no event in common.
There doesn't *have* to be a connection between the events of independent
listens, but some streams do have a connection. Each stream gets to decide
for itself how it behaves when you listen to it. There are many options,
anyone creating a stream should pick the behavior that best matches the
task at hand.
The stream controller is a way to feed events to a stream listener. The
controllers in the platform libraries are built to ensure that events fed
into the controller will be delivered in the same order to the listener. We
are permissive and allow you to add events while paused (we could have
decided to not allow that), and if you do that, the event is buffered.
That's a property of the controller and not the stream - the stream just
delivers events as it's being instructed to. Buffering in that case is a
choice, chosen by the owner of the controller.
Post by tatumizer-v0.2So we have a buffer inside stream, and another buffer - in subscription
(which allows to 'pause' and 'resume')?
Streams deliver events to subscribers. Listeners can pause, resume and
cancel. Whoever generates the events should honor that. If you use a
StreamController to feed the stream subscription, you should still honor
pauses (but we help you by buffering if you decide not to, because in some
cases, it's actually convenient).
Post by tatumizer-v0.2But.... consider stdin - it's a stream. When we listen to it, we don't
expect to get all the key events since yesterday (for every invocation of
listen()).
Probably not. But if you pause the subscription, you expect to get the
remaining characters in the stream. Otherwise things like `await for` won't
work anymore. They may pause the input stream when they are blocked on
other asynchronous operations:
await for (var x in someStream) {
await handleData(x); // May pause `someStream`.
}
Assuming a "pause" happens, then the subscription sends a "pause" request.
If that request isn't honored (shouldn't happen but may), then the
subscription still makes sure that no other event happens or is lost.
Otherwise the `await for` would be really weird.
We (or the dart:io library designers) need to decide what the behavior of
stdin is. Should it allow multiple concurrent listeners? Should it buffer
while nobody listens? What is the most reasonable *behavior* of a stream
representing stdin? With Dart 2 streams, anything is possible, you don't
have to constrain yourself to either single-subscription or broadcast.
Post by tatumizer-v0.2So we have 2 sorts of streams - with memory and without? Or maybe 3rd type
is: stream with memory, but the memory saves data only until the first
listener connects?
Similar to Iterables, there are *many* different kinds of streams.
Buffering is an implementation detail. For some behaviors, it's necessary.
For other behaviors it's optional. For some it's completely unnecessary.
Each stream should do what works best for the problem it's trying to solve.
Fitting everything into either single-subscription or broadcast didn't
allow that, so we remove the restrictions that limit you to those two
options.
/L
--
Lasse R.H. Nielsen - ***@google.com
'Faith without judgement merely degrades the spirit divine'
Google Denmark ApS - Frederiksborggade 20B, 1 sal - 1360 KÞbenhavn K
- Denmark - CVR nr. 28 86 69 84
--
For other discussions, see https://groups.google.com/a/dartlang.org/
For HOWTO questions, visit http://stackoverflow.com/tags/dart
To file a bug report or feature request, go to http://www.dartbug.com/new
---
You received this message because you are subscribed to the Google Groups "Dart Misc" group.
To unsubscribe from this group and stop receiving emails from it, send an email to misc+***@dartlang.org.