@@ -10,7 +10,10 @@ import "package:async/async.dart";
10
10
11
11
/// Handles rate-limited scheduling of tasks.
12
12
///
13
- /// Designed to allow prefetching tasks that will likely be needed
13
+ /// Tasks are named with a key of type [K] (should be useful as a Hash-key) and
14
+ /// run with a supplied function producing a CancelableOperation.
15
+ ///
16
+ /// Designed to allow prefetching of tasks that will likely be needed
14
17
/// later with [prefetch] .
15
18
///
16
19
/// All current operations can be cancelled and future operations removed from
@@ -33,29 +36,43 @@ import "package:async/async.dart";
33
36
/// final pubDevBody = await retriever.fetch(Uri.parse('https://pub.dev/'));
34
37
/// ```
35
38
class Retriever <K , V > {
36
- final CancelableOperation <V > Function (K , Retriever ) _get;
39
+ final CancelableOperation <V > Function (K , Retriever ) _run;
40
+
41
+ /// The results of ongoing and finished computations.
37
42
final Map <K , Completer <V >> _cache = < K , Completer <V >> {};
38
43
39
44
/// Operations that are waiting to run.
40
45
final Queue <K > _queue = Queue <K >();
41
46
47
+ /// Rate limits the downloads.
42
48
final Pool _pool;
43
49
44
- /// The active operations
50
+ /// The currently active operations.
45
51
final Map <K , CancelableOperation <V >> _active = < K , CancelableOperation <V >> {};
46
- bool started = false ;
47
52
48
- Retriever (this ._get, {maxConcurrentOperations = 10 })
49
- : _pool = Pool (maxConcurrentOperations);
53
+ /// True when the processing loop is running.
54
+ bool _started = false ;
55
+
56
+ Retriever (CancelableOperation <V > Function (K , Retriever ) run,
57
+ {maxConcurrentOperations = 10 })
58
+ : _run = run,
59
+ _pool = Pool (maxConcurrentOperations);
60
+
61
+ Retriever .nonCancelable (Future <V > Function (K , Retriever ) run,
62
+ {maxConcurrentOperations = 10 })
63
+ : this (
64
+ (key, retriever) =>
65
+ CancelableOperation .fromFuture (run (key, retriever)),
66
+ maxConcurrentOperations: maxConcurrentOperations);
50
67
51
68
/// Starts running operations from the queue. Taking the first items first.
52
69
void _process () async {
53
- assert (! started );
54
- started = true ;
70
+ assert (! _started );
71
+ _started = true ;
55
72
while (_queue.isNotEmpty) {
56
73
final resource = await _pool.request ();
57
74
// This checks if [stop] has been called while waiting for a resource.
58
- if (! started ) {
75
+ if (! _started ) {
59
76
resource.release ();
60
77
break ;
61
78
}
@@ -75,8 +92,8 @@ class Retriever<K, V> {
75
92
continue ;
76
93
}
77
94
78
- // Run operation task.
79
- final operation = _get (task, this );
95
+ // Start running the operation for [ task] .
96
+ final operation = _run (task, this );
80
97
_active[task] = operation;
81
98
operation
82
99
.then (completer.complete, onError: completer.completeError)
@@ -86,42 +103,42 @@ class Retriever<K, V> {
86
103
_active.remove (task);
87
104
});
88
105
}
89
- started = false ;
106
+ _started = false ;
90
107
}
91
108
92
109
/// Cancels all active computations, and clears the queue.
93
110
void stop () {
94
- // Stop the processing loop
95
- started = false ;
96
- // Cancel all active operatios
111
+ // Stop the processing loop.
112
+ _started = false ;
113
+ // Cancel all active operations.
97
114
for (final operation in _active.values) {
98
115
operation.cancel ();
99
116
}
100
- // Do not process anymore .
117
+ // Do not process the rest of the queue .
101
118
_queue.clear ();
102
119
}
103
120
104
121
/// Puts [task] in the back of the work queue.
105
122
///
106
- /// Tasl will be processed when there are free resources, and other already
123
+ /// Task will be processed when there are free resources, and other already
107
124
/// queued tasks are done.
108
125
void prefetch (K task) {
109
126
_queue.addLast (task);
110
- if (! started ) _process ();
127
+ if (! _started ) _process ();
111
128
}
112
129
113
130
/// Returns the result of running [task] .
114
131
///
115
132
/// If [task] is already done, the cached result will be returned.
116
133
/// If [task] is not yet active, it will go to the front of the work queue
117
- /// to be scheduled when there are free resources.
134
+ /// to be scheduled next when there are free resources.
118
135
Future <V > fetch (K task) {
119
136
final completer = _cache.putIfAbsent (task, () => Completer ());
120
137
if (! completer.isCompleted) {
121
- // We don't worry about adding the same task twice.
138
+ // We allow adding the same task twice to the queue .
122
139
// It will get dedupped by the [_process] loop.
123
140
_queue.addFirst (task);
124
- if (! started ) _process ();
141
+ if (! _started ) _process ();
125
142
}
126
143
return completer.future;
127
144
}
0 commit comments