You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/AsyncSeq.fsx
+18-28Lines changed: 18 additions & 28 deletions
Original file line number
Diff line number
Diff line change
@@ -17,6 +17,8 @@
17
17
18
18
# F# Async: FSharp.Control.AsyncSeq
19
19
20
+
> NOTE: There is also the option to use [FSharp.Control.TaskSeq](https://github.com/fsprojects/FSharp.Control.TaskSeq) which has a very similar usage model.
21
+
20
22
An AsyncSeq is a sequence in which individual elements are retrieved using an `Async` computation.
21
23
It is similar to `seq<'a>` in that subsequent elements are pulled on-demand.
22
24
`AsyncSeq` also bears similarity to `IObservable<'a>` with the former being based on an "asynchronous pull" and the
@@ -30,15 +32,13 @@ The `AsyncSeq` type is located in the `FSharp.Control.AsyncSeq.dll` assembly whi
30
32
#r "../../../bin/FSharp.Control.AsyncSeq.dll"
31
33
openFSharp.Control
32
34
33
-
34
-
35
35
(**
36
36
### Generating asynchronous sequences
37
37
38
-
An `AsyncSeq<'a>` can be generated using computation expression syntax much like `seq<'a>`:
38
+
An `AsyncSeq<'T>` can be generated using computation expression syntax much like `seq<'T>`:
39
39
*)
40
40
41
-
letasyncS= asyncSeq {
41
+
letasync12= asyncSeq {
42
42
yield1
43
43
yield2
44
44
}
@@ -123,19 +123,19 @@ composed using familiar operations on sequences. Furthermore, it will be execute
123
123
*)
124
124
125
125
(**
126
-
### Comparison with seq<'a>
126
+
### Comparison with seq<'T>
127
127
128
-
The central difference between `seq<'a>` and `AsyncSeq<'a>` can be illustrated by introducing the notion of time.
128
+
The central difference between `seq<'T>` and `AsyncSeq<'T>` can be illustrated by introducing the notion of time.
129
129
Suppose that generating subsequent elements of a sequence requires an IO-bound operation. Invoking long
130
-
running IO-bound operations from within a `seq<'a>` will *block* the thread which calls `MoveNext` on the
130
+
running IO-bound operations from within a `seq<'T>` will *block* the thread which calls `MoveNext` on the
131
131
corresponding `IEnumerator`. An `AsyncSeq` on the other hand can use facilities provided by the F# `Async` type to make
132
132
more efficient use of system resources.
133
133
*)
134
134
135
135
letwithTime=seq{
136
-
System.Threading.Thread.Sleep(1000)// calling thread will block
136
+
Thread.Sleep(1000)// calling thread will block
137
137
yield1
138
-
System.Threading.Thread.Sleep(1000)// calling thread will block
138
+
Thread.Sleep(1000)// calling thread will block
139
139
yield1
140
140
}
141
141
@@ -151,22 +151,19 @@ When the asynchronous sequence `withTime'` is iterated, the calls to `Async.Slee
151
151
the *continuation* of the sequence will be scheduled by `Async` while the calling thread will be free to perform other work.
152
152
Overall, a `seq<'a>` can be viewed as a special case of an `AsyncSeq<'a>` where subsequent elements are retrieved
153
153
in a blocking manner.
154
-
*)
155
154
155
+
### Comparison with IObservable<'T>
156
156
157
-
(**
158
-
### Comparison with IObservable<'a>
159
-
160
-
Both `IObservable<'a>` and `AsyncSeq<'a>` represent collections of items and both provide similar operations
157
+
Both `IObservable<'T>` and `AsyncSeq<'T>` represent collections of items and both provide similar operations
161
158
for transformation and composition. The central difference between the two is that the former uses a *synchronous push*
162
159
to a subscriber and the latter uses an *asynchronous pull* by a consumer.
163
-
Consumers of an `IObservable<'a>`*subscribe* to receive notifications about
164
-
new items or the end of the sequence. By contrast, consumers of an `AsyncSeq<'a>`*asynchronously retrieve* subsequent items on their own
160
+
Consumers of an `IObservable<'T>`*subscribe* to receive notifications about
161
+
new items or the end of the sequence. By contrast, consumers of an `AsyncSeq<'T>`*asynchronously retrieve* subsequent items on their own
165
162
terms. Some domains are more naturally modeled with one or the other, however it is less clear which is a more
166
163
suitable tool for a specific task. In many cases, a combination of the two provides the optimal solution and
167
164
restricting yourself to one, while simplifying the programming model, can lead one to view all problems as a nail.
168
165
169
-
A more specific difference between the two is that `IObservable<'a>` subscribers have the basic type `'a -> unit`
166
+
A more specific difference between the two is that `IObservable<'T>` subscribers have the basic type `'T -> unit`
170
167
and are therefore inherently synchronous and imperative. The observer can certainly make a blocking call, but this
171
168
can defeat the purpose of the observable sequence all together. Alternatively, the observer can spawn an operation, but
172
169
this can break composition because one can no longer rely on the observer returning to determine that it has
@@ -177,7 +174,7 @@ To illustrate, let's try to implement the above Tweet retrieval, filtering and s
177
174
Suppose we already have an observable sequence representing tweets `IObservable<Tweet>` and we simply wish
178
175
to filter it and store the resulting tweets. The function `Observable.filter` allows one to filter observable
179
176
sequences based on a predicate, however in this case it doesn't quite cut it because the predicate passed to it must
To remedy the blocking IO-call we can better adapt the filtering function to the `IObservable<'a>` model. A value
194
-
of type `Async<'a>` can be modeled as an `IObservable<'a>` with one element. Suppose that we have
190
+
To remedy the blocking IO-call we can better adapt the filtering function to the `IObservable<'T>` model. A value
191
+
of type `Async<'T>` can be modeled as an `IObservable<'T>` with one element. Suppose that we have
195
192
`Tweet -> IObservable<bool>`. We can define a few helper operators on observables to allow filtering using
196
193
an asynchronous predicate as follows:
197
194
*)
@@ -262,13 +259,10 @@ notifications must be pushed immediately without delay.
262
259
263
260
Upon closer inspection, the consumption approaches between the two models aren't all too different. While `AsyncSeq` is based on an asynchronous-pull operation,
264
261
it is usually consumed using an operator such as `AsyncSeq.iterAsync` as shown above. This is a function of type
265
-
`('a -> Async<unit>) -> AsyncSeq<'a> -> Async<unit>` where the first argument is a function `'a -> Async<unit>` which performs
262
+
`('T -> Async<unit>) -> AsyncSeq<'T> -> Async<unit>` where the first argument is a function `'T -> Async<unit>` which performs
266
263
some work on an item of the sequence and is applied repeatedly to subsequent items. In a sense, `iterAsync`*pushes* values to this
267
264
function. The primary difference from observers of observable sequences is the return type `Async<unit>` rather than simply `unit`.
268
-
*)
269
265
270
-
271
-
(**
272
266
### Performance Considerations
273
267
274
268
While an asynchronous computation obviates the need to block an OS thread for the duration of an operation, it isn't always the case
@@ -277,10 +271,6 @@ non-blocking operation, it simply allows for it. Also of note is that unlike cal
277
271
an item from an asynchronous sequence requires several allocations. Usually this is greatly outweighed by the
278
272
benefits, it can make a difference in some scenarios.
279
273
280
-
*)
281
-
282
-
283
-
(**
284
274
## Related Articles
285
275
286
276
*[Programming with F# asynchronous sequences](http://tomasp.net/blog/async-sequences.aspx/)
0 commit comments