1 /**
2  * Promise range tools.
3  *
4  * License:
5  *   This Source Code Form is subject to the terms of
6  *   the Mozilla Public License, v. 2.0. If a copy of
7  *   the MPL was not distributed with this file, You
8  *   can obtain one at http://mozilla.org/MPL/2.0/.
9  *
10  * Authors:
11  *   Vladimir Panteleev <ae@cy.md>
12  */
13 
14 module ae.utils.promise.range;
15 
16 import std.range.primitives;
17 
18 import ae.net.asockets : socketManager;
19 import ae.utils.promise;
20 
21 /// Given a range of promises, resolve them one after another,
22 /// and return a promise which is fulfilled when all promises in `range` are fulfilled.
23 /// `range` may be a lazy range (e.g. a `map` which produces promises from other input),
24 /// which will cause the work to be started only when the previous promise completes.
25 PromiseValueTransform!(ElementType!R, x => [x]) allSerial(R)(R range)
26 if (isInputRange!R)
27 {
28 	auto p = new typeof(return);
29 
30 	alias P = ElementType!R;
31 	alias T = PromiseValue!P;
32 	alias E = PromiseError!P;
33 
34 	typeof(p).ValueTuple results;
35 	static if (!is(T == void))
36 	{
37 		static if (hasLength!R)
38 			results[0].reserve(range.length);
39 	}
40 
41 	void next()
42 	{
43 		if (range.empty)
44 			p.fulfill(results);
45 		else
46 		{
47 			range.front.then((P.ValueTuple value) {
48 				static if (!is(T == void))
49 					results[0] ~= value[0];
50 				next();
51 			}, (E error) {
52 				p.reject(error);
53 			});
54 			range.popFront();
55 		}
56 	}
57 
58 	next();
59 
60 	return p;
61 }
62 
63 unittest
64 {
65 	import std.algorithm.iteration : map;
66 	import ae.sys.timing : setTimeout;
67 	import core.time : seconds;
68 
69 	size_t sum;
70 	[1, 2, 3]
71 		.map!((n) {
72 			auto nextSum = sum + n;
73 			auto p = new Promise!void();
74 			setTimeout({ sum = nextSum; p.fulfill(); }, 0.seconds);
75 			return p;
76 		})
77 		.allSerial;
78 	socketManager.loop();
79 	assert(sum == 6);
80 }