1 /**
2  * async/await-like API for asynchronous tasks combining promises and
3  * fibers.
4  *
5  * License:
6  *   This Source Code Form is subject to the terms of
7  *   the Mozilla Public License, v. 2.0. If a copy of
8  *   the MPL was not distributed with this file, You
9  *   can obtain one at http://mozilla.org/MPL/2.0/.
10  *
11  * Authors:
12  *   Vladimir Panteleev <ae@cy.md>
13  */
14 
15 module ae.utils.promise.await;
16 
17 import core.thread : Fiber;
18 
19 import ae.utils.promise;
20 
21 /// Evaluates `task` in a new fiber, and returns a promise which is
22 /// fulfilled when `task` exits.  `task` may use `await` to block on
23 /// other promises.
24 Promise!(T, E) async(T, E = Exception)(lazy T task)
25 {
26 	auto p = new Promise!T;
27 	auto f = new Fiber({
28 		try
29 			static if (is(T == void))
30 				task, p.fulfill();
31 			else
32 				p.fulfill(task);
33 		catch (E e)
34 			p.reject(e);
35 	}, 64 * 1024);
36 	f.call();
37 	return p;
38 }
39 
40 /// Synchronously waits until the promise `p` is fulfilled.
41 /// Can only be called in a fiber.
42 T await(T, E)(Promise!(T, E) p)
43 {
44 	Promise!T.ValueTuple fiberValue;
45 	E fiberError;
46 
47 	auto f = Fiber.getThis();
48 	p.then((Promise!T.ValueTuple value) {
49 		fiberValue = value;
50 		f.call();
51 	}, (E error) {
52 		fiberError = error;
53 		f.call();
54 	});
55 	Fiber.yield();
56 	if (fiberError)
57 		throw fiberError;
58 	else
59 	{
60 		static if (!is(T == void))
61 			return fiberValue[0];
62 	}
63 }
64 
65 ///
66 unittest
67 {
68 	import ae.net.asockets : socketManager;
69 
70 	auto one = resolve(1);
71 	auto two = resolve(2);
72 
73 	int sum;
74 	async(one.await + two.await).then((value) {
75 		sum = value;
76 	});
77 	socketManager.loop();
78 	assert(sum == 3);
79 }
80 
81 unittest
82 {
83 	if (false)
84 	{
85 		async({}()).await();
86 	}
87 }