1 /+ 2 JS-like promise 3 +/ 4 module sily.async.promise; 5 6 import std.net.curl: HTTPStatusException; 7 import std.concurrency; 8 import core.thread; 9 10 /// Wrapper for Curl requests 11 alias HTTPRequest = Promise!(string, HTTPStatusException); 12 13 private struct PromiseHandler { 14 void delegate() handler; 15 bool onFullfill; 16 bool onReject; 17 } 18 19 /++ 20 Simple implementation of JavaScript promises 21 Example: 22 --- 23 HTTPRequest prom = new Promise!(string, HTTPStatusException)(); 24 25 prom.then(delegate void(string s) { 26 writeln(s); 27 }).then(null, delegate void(HTTPStatusException e) { 28 writeln("Error ", e.status, ": ", e.msg); 29 }).except(delegate void(HTTPStatusException e) { 30 writeln(e.msg); 31 }).finish(delegate void() { 32 writeln("Finished after error"); 33 }); 34 35 prom.resolve("My data"); 36 prom.refresh(); 37 prom.reject(new HTTPStatusException(451, "Reject message")); 38 --- 39 +/ 40 final class Promise(T, E: Throwable = Exception) { 41 42 private PromiseHandler[] _handlers; 43 44 private PromiseState _state; 45 46 private alias ThisType = Promise!(T, E); 47 48 private struct BlackBox { 49 static if (!is(T == void)) T value; 50 } 51 52 private BlackBox _value; 53 private E _error; 54 55 alias V = typeof(BlackBox.tupleof); 56 57 /// Resolves promise and calles `then onResolve` callbacks 58 private void resolve(BlackBox val) { 59 _state = PromiseState.fulfilled; 60 _value = val; 61 foreach (func; _handlers) { 62 if (func.onFullfill) { 63 func.handler(); 64 } 65 } 66 _handlers = null; 67 } 68 69 /// Ditto 70 void resolve() { 71 resolve(BlackBox()); 72 } 73 74 static if (!is(T == void)) { 75 /// Resolves promise and calles `then onResolve` callbacks 76 void resolve(T val) { 77 resolve(BlackBox(val)); 78 } 79 } 80 81 /// Resolves promise and calles `then onError` callback 82 void reject(E err) { 83 _state = PromiseState.rejected; 84 _error = err; 85 foreach (func; _handlers) { 86 if (func.onReject) { 87 func.handler(); 88 } 89 } 90 _handlers = null; 91 } 92 93 private void tryResolve(R, S)(R delegate(S) callback, S val) { 94 static if (is(R == void)) { 95 static if (is(S == void)) { 96 callback(); 97 resolve(); 98 } else { 99 callback(val); 100 resolve(); 101 } 102 } else { 103 static if (is(S == void)) { 104 resolve(callback()); 105 } else { 106 resolve(callback(val)); 107 } 108 } 109 } 110 111 private void tryResolve(R)(R delegate() callback) { 112 static if (is(R == void)) { 113 callback(); 114 resolve(); 115 } else { 116 resolve(callback()); 117 } 118 } 119 120 /// Registers on resolve functions (set null for no callback) 121 Promise!(S, F) then(S, F = E)(S delegate(V) onResolve, S delegate(E) onReject = null) { 122 Promise!(S, F) next = new Promise!(S, F); 123 124 void resolveHandler() { 125 if (onResolve !is null) { 126 try { 127 // next.resolve(onResolve(_value.tupleof)); 128 next.tryResolve(onResolve, _value.tupleof); 129 } catch (F e) { 130 next.reject(e); 131 } 132 } else { 133 next.resolve(); 134 // static if (is(S == void)) { 135 // next.resolve(); 136 // } else { 137 // next.resolve(_value.tupleof); 138 // } 139 } 140 } 141 142 void rejectHandler() { 143 if (onReject !is null) { 144 try { 145 // next.resolve(onReject(_error)); 146 next.tryResolve(onReject, _error); 147 } catch (F e) { 148 next.reject(e); 149 } 150 } else { 151 next.reject(_error); 152 } 153 } 154 155 switch (_state) { 156 case PromiseState.pending: 157 _handlers ~= PromiseHandler(&resolveHandler, true, false); 158 _handlers ~= PromiseHandler(&rejectHandler, false, true); 159 break; 160 case PromiseState.fulfilled: 161 resolveHandler(); 162 break; 163 case PromiseState.rejected: 164 rejectHandler(); 165 break; 166 default: break; 167 } 168 169 return next; 170 } 171 172 /// Registers catch callback, equivalent to `then(null, ErrorCallback)` 173 Promise!(S, F) except(S, F = E)(S delegate(F) onCatch) { 174 return this.then(null, onCatch); 175 } 176 177 /// Registers callback to be called at end. Equivalent to `then(onFinally, onFinally)` 178 Promise!(S, E) finish(S)(S delegate(V) onResolve) { 179 Promise!(S, E) next = new Promise!(S, E); 180 181 void resolveHandler() { 182 if (onResolve !is null) { 183 try { 184 // next.resolve(onResolve(_value.tupleof)); 185 next.tryResolve(onResolve, _value.tupleof); 186 } catch (E e) { 187 next.reject(e); 188 } 189 } else { 190 static if (is(S == void)) { 191 next.resolve(); 192 } else { 193 next.resolve(_value.tupleof); 194 } 195 } 196 } 197 198 switch (_state) { 199 case PromiseState.pending: 200 _handlers ~= PromiseHandler(&resolveHandler, true, true); 201 break; 202 case PromiseState.fulfilled: 203 resolveHandler(); 204 break; 205 case PromiseState.rejected: 206 resolveHandler(); 207 break; 208 default: break; 209 } 210 211 return next; 212 } 213 } 214 215 private enum PromiseState { 216 pending, 217 fulfilled, 218 rejected 219 } 220 221