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 enum defaultFiberSize = 64 * 1024; 22 23 /// Evaluates `task` in a new fiber, and returns a promise which is 24 /// fulfilled when `task` exits. `task` may use `await` to block on 25 /// other promises. 26 // TODO: is using lazy OK for this? https://issues.dlang.org/show_bug.cgi?id=23923 27 Promise!(T, E) async(T, E = Exception)(lazy T task, size_t size = defaultFiberSize) 28 if (!is(T == return)) 29 { 30 return async({ return task; }, size); 31 } 32 33 /// ditto 34 Promise!(T, E) async(T, E = Exception)(T delegate() task, size_t size = defaultFiberSize) 35 if (!is(T == return)) 36 { 37 auto p = new Promise!T; 38 auto f = new Fiber({ 39 try 40 static if (is(T == void)) 41 task(), p.fulfill(); 42 else 43 p.fulfill(task()); 44 catch (E e) 45 p.reject(e); 46 }, size); 47 f.call(); 48 return p; 49 } 50 51 /// ditto 52 Promise!(T, E) async(T, E = Exception)(T function() task) 53 if (!is(T == return)) 54 { 55 import std.functional : toDelegate; 56 return threadAsync(task.toDelegate); 57 } 58 59 /// Synchronously waits until the promise `p` is fulfilled. 60 /// Can only be called in a fiber. 61 T await(T, E)(Promise!(T, E) p) 62 { 63 Promise!T.ValueTuple fiberValue; 64 E fiberError; 65 66 auto f = Fiber.getThis(); 67 assert(f, "await called while not in a fiber"); 68 p.then((Promise!T.ValueTuple value) { 69 fiberValue = value; 70 f.call(); 71 }, (E error) { 72 fiberError = error; 73 f.call(); 74 }); 75 Fiber.yield(); 76 if (fiberError) 77 throw fiberError; 78 else 79 { 80 static if (!is(T == void)) 81 return fiberValue[0]; 82 } 83 } 84 85 /// 86 unittest 87 { 88 import ae.net.asockets : socketManager; 89 90 auto one = resolve(1); 91 auto two = resolve(2); 92 93 int sum; 94 async(one.await + two.await).then((value) { 95 sum = value; 96 }); 97 socketManager.loop(); 98 assert(sum == 3); 99 } 100 101 unittest 102 { 103 if (false) 104 { 105 async({}()).await(); 106 } 107 }