comparison Implab/PromiseExtensions.cs @ 248:5cb4826c2c2a v3

Added awaiters to promises Added static methods to Promise Resolve, Reject, All. Updated promise helpers
author cin
date Tue, 30 Jan 2018 01:37:17 +0300
parents a867536c68fc
children d82909310094
comparison
equal deleted inserted replaced
247:fb70574741a1 248:5cb4826c2c2a
4 using System.Collections.Generic; 4 using System.Collections.Generic;
5 using System.Linq; 5 using System.Linq;
6 6
7 namespace Implab { 7 namespace Implab {
8 public static class PromiseExtensions { 8 public static class PromiseExtensions {
9 public static IPromise<T> DispatchToCurrentContext<T>(this IPromise<T> that) {
10 Safe.ArgumentNotNull(that, "that");
11 var context = SynchronizationContext.Current;
12 if (context == null)
13 return that;
14 9
15 var p = new SyncContextPromise<T>(context); 10 public static IPromise Then(this IPromise that, Action fulfilled, Action<Exception> rejected) {
16 p.CancellationRequested(that.Cancel); 11 var reaction = new PromiseActionReaction(fulfilled, rejected, Promise.DefaultDispatcher);
17 12 that.Then(reaction);
18 that.On( 13 return reaction.Promise;
19 p.Resolve,
20 p.Reject,
21 p.CancelOperation
22 );
23 return p;
24 } 14 }
25 15
26 public static IPromise<T> DispatchToContext<T>(this IPromise<T> that, SynchronizationContext context) { 16 public static IPromise Then(this IPromise that, Action fulfilled, Func<Exception, IPromise> rejected) {
27 Safe.ArgumentNotNull(that, "that"); 17 var reaction = new PromiseActionReaction(fulfilled, rejected, Promise.DefaultDispatcher);
28 Safe.ArgumentNotNull(context, "context"); 18 that.Then(reaction);
29 19 return reaction.Promise;
30 var p = new SyncContextPromise<T>(context);
31 p.CancellationRequested(that.Cancel);
32
33 that.On(
34 p.Resolve,
35 p.Reject,
36 p.CancelOperation
37 );
38 return p;
39 } 20 }
40 21
41 /// <summary> 22 public static IPromise Then(this IPromise that, Func<IPromise> fulfilled, Action<Exception> rejected) {
42 /// Ensures the dispatched. 23 var reaction = new PromiseActionReaction(fulfilled, rejected, Promise.DefaultDispatcher);
43 /// </summary> 24 that.Then(reaction);
44 /// <returns>The dispatched.</returns> 25 return reaction.Promise;
45 /// <param name="that">That.</param>
46 /// <param name="head">Head.</param>
47 /// <param name="cleanup">Cleanup.</param>
48 /// <typeparam name="TPromise">The 1st type parameter.</typeparam>
49 /// <typeparam name="T">The 2nd type parameter.</typeparam>
50 public static TPromise EnsureDispatched<TPromise, T>(this TPromise that, IPromise<T> head, Action<T> cleanup) where TPromise : IPromise {
51 Safe.ArgumentNotNull(that, "that");
52 Safe.ArgumentNotNull(head, "head");
53
54 that.On(() => head.On(cleanup), PromiseEventType.Cancelled);
55
56 return that;
57 } 26 }
58 27
59 /// <summary> 28 public static IPromise Then(this IPromise that, Func<IPromise> fulfilled, Func<Exception, IPromise> rejected) {
60 /// Adds a cancellation point to the chain of promises. When a cancellation request reaches the cancellation point the operation is 29 var reaction = new PromiseActionReaction(fulfilled, rejected, Promise.DefaultDispatcher);
61 /// cancelled immediatelly, and the request is passed towards. If the operation at the higher level can not be cancelled is't result 30 that.Then(reaction);
62 /// will be collected with <paramref name="cleanup"/> callback. 31 return reaction.Promise;
63 /// </summary>
64 /// <typeparam name="T">The type of the promise result.</typeparam>
65 /// <param name="that">The promise to which the cancellation point should be attached.</param>
66 /// <param name="cleanup">The callback which is used to cleanup the result of the operation if the cancellation point is cancelled already.</param>
67 /// <returns>The promise</returns>
68 public static IPromise<T> CancellationPoint<T>(this IPromise<T> that, Action<T> cleanup) {
69 var meduim = new Promise<T>();
70
71 that.On(meduim.Resolve, meduim.Reject, meduim.CancelOperation);
72
73 meduim.CancellationRequested(that.Cancel);
74 meduim.CancellationRequested(meduim.CancelOperation);
75
76 if (cleanup != null)
77 meduim.On((Action<T>)null, null, (e) => {
78 that.On(cleanup);
79 });
80
81 return meduim;
82 } 32 }
83 33
84 public static AsyncCallback AsyncCallback<T>(this Promise<T> that, Func<IAsyncResult, T> callback) { 34 public static IPromise Then<T>(this IPromise<T> that, Action<T> fulfilled, Action<Exception> rejected) {
85 Safe.ArgumentNotNull(that, "that"); 35 var reaction = new PromiseActionReaction<T>(fulfilled, rejected, Promise.DefaultDispatcher);
86 Safe.ArgumentNotNull(callback, "callback"); 36 that.Then(reaction);
87 var op = TraceContext.Instance.CurrentOperation; 37 return reaction.Promise;
88 return ar => {
89 TraceContext.Instance.EnterLogicalOperation(op, false);
90 try {
91 that.Resolve(callback(ar));
92 } catch (Exception err) {
93 that.Reject(err);
94 } finally {
95 TraceContext.Instance.Leave();
96 }
97 };
98 } 38 }
99 39
100 static void CancelByTimeoutCallback(object cookie) { 40 public static IPromise Then<T>(this IPromise<T> that, Action<T> fulfilled, Func<Exception, IPromise> rejected) {
101 ((ICancellable)cookie).Cancel(new TimeoutException()); 41 var reaction = new PromiseActionReaction<T>(fulfilled, rejected, Promise.DefaultDispatcher);
42 that.Then(reaction);
43 return reaction.Promise;
102 } 44 }
103 45
104 /// <summary> 46 public static IPromise Then<T>(this IPromise<T> that, Func<T, IPromise> fulfilled, Action<Exception> rejected) {
105 /// Cancells promise after the specified timeout is elapsed. 47 var reaction = new PromiseActionReaction<T>(fulfilled, rejected, Promise.DefaultDispatcher);
106 /// </summary> 48 that.Then(reaction);
107 /// <param name="that">The promise to cancel on timeout.</param> 49 return reaction.Promise;
108 /// <param name="milliseconds">The timeout in milliseconds.</param>
109 /// <typeparam name="TPromise">The 1st type parameter.</typeparam>
110 public static TPromise Timeout<TPromise>(this TPromise that, int milliseconds) where TPromise : IPromise {
111 Safe.ArgumentNotNull(that, "that");
112 var timer = new Timer(CancelByTimeoutCallback, that, milliseconds, -1);
113 that.On(timer.Dispose, PromiseEventType.All);
114 return that;
115 } 50 }
116 51
117 public static IPromise PromiseAll(this IEnumerable<IPromise> that) { 52 public static IPromise Then<T>(this IPromise<T> that, Func<T, IPromise> fulfilled, Func<Exception, IPromise> rejected) {
118 Safe.ArgumentNotNull(that, "that"); 53 var reaction = new PromiseActionReaction<T>(fulfilled, rejected, Promise.DefaultDispatcher);
119 return PromiseAll(that.ToList()); 54 that.Then(reaction);
55 return reaction.Promise;
120 } 56 }
121 57
122 public static IPromise<T[]> PromiseAll<T>(this IEnumerable<IPromise<T>> that) { 58 public static IPromise<Tout> Then<Tout>(this IPromise that, Func<Tout> fulfilled, Func<Exception, Tout> rejected) {
123 return PromiseAll(that, null); 59 var reaction = new PromiseFuncReaction<Tout>(fulfilled, rejected, Promise.DefaultDispatcher);
60 that.Then(reaction);
61 return reaction.Promise;
124 } 62 }
125 63
126 public static IPromise<T[]> PromiseAll<T>(this IEnumerable<IPromise<T>> that, Action<T> cleanup) { 64 public static IPromise<Tout> Then<Tout>(this IPromise that, Func<Tout> fulfilled, Func<Exception, IPromise<Tout>> rejected) {
127 Safe.ArgumentNotNull(that, "that"); 65 var reaction = new PromiseFuncReaction<Tout>(fulfilled, rejected, Promise.DefaultDispatcher);
128 return PromiseAll(that.ToList(), cleanup); 66 that.Then(reaction);
67 return reaction.Promise;
129 } 68 }
130 69
131 public static IPromise PromiseAll(this ICollection<IPromise> that) { 70 public static IPromise<Tout> Then<Tout>(this IPromise that, Func<IPromise<Tout>> fulfilled, Func<Exception, Tout> rejected) {
132 Safe.ArgumentNotNull(that, "that"); 71 var reaction = new PromiseFuncReaction<Tout>(fulfilled, rejected, Promise.DefaultDispatcher);
133 72 that.Then(reaction);
134 int count = that.Count; 73 return reaction.Promise;
135 int errors = 0;
136 var medium = new Promise();
137
138 if (count == 0) {
139 medium.Resolve();
140 return medium;
141 }
142
143 medium.On(() => {
144 foreach (var p2 in that)
145 p2.Cancel();
146 }, PromiseEventType.ErrorOrCancel);
147
148 foreach (var p in that)
149 p.On(
150 () => {
151 if (Interlocked.Decrement(ref count) == 0)
152 medium.Resolve();
153 },
154 error => {
155 if (Interlocked.Increment(ref errors) == 1)
156 medium.Reject(
157 new Exception("The dependency promise is failed", error)
158 );
159 },
160 reason => {
161 if (Interlocked.Increment(ref errors) == 1)
162 medium.Cancel(
163 new Exception("The dependency promise is cancelled")
164 );
165 }
166 );
167
168 return medium;
169 } 74 }
170 75
171 public static IPromise<T[]> PromiseAll<T>(this ICollection<IPromise<T>> that) { 76 public static IPromise<Tout> Then<Tout>(this IPromise that, Func<IPromise<Tout>> fulfilled, Func<Exception, IPromise<Tout>> rejected) {
172 return PromiseAll(that, null); 77 var reaction = new PromiseFuncReaction<Tout>(fulfilled, rejected, Promise.DefaultDispatcher);
78 that.Then(reaction);
79 return reaction.Promise;
173 } 80 }
174 81
175 /// <summary> 82 public static IPromise<Tout> Then<Tin, Tout>(this IPromise<Tin> that, Func<Tin, Tout> fulfilled, Func<Exception, Tout> rejected) {
176 /// Creates a new promise which will be satisfied when all promises are satisfied. 83 var reaction = new PromiseFuncReaction<Tin, Tout>(fulfilled, rejected, Promise.DefaultDispatcher);
177 /// </summary> 84 that.Then(reaction);
178 /// <typeparam name="T"></typeparam> 85 return reaction.Promise;
179 /// <param name="that"></param>
180 /// <param name="cleanup">A callback used to cleanup already resolved promises in case of an error</param>
181 /// <returns></returns>
182 public static IPromise<T[]> PromiseAll<T>(this ICollection<IPromise<T>> that, Action<T> cleanup) {
183 Safe.ArgumentNotNull(that, "that");
184
185 int count = that.Count;
186
187 if (count == 0)
188 return Promise<T[]>.FromResult(new T[0]);
189
190 int errors = 0;
191 var medium = new Promise<T[]>();
192 var results = new T[that.Count];
193
194 medium.On(() => {
195 foreach (var p2 in that) {
196 p2.Cancel();
197 if (cleanup != null)
198 p2.On(cleanup);
199 }
200 }, PromiseEventType.ErrorOrCancel);
201
202 int i = 0;
203 foreach (var p in that) {
204 var idx = i;
205 p.On(
206 x => {
207 results[idx] = x;
208 if (Interlocked.Decrement(ref count) == 0)
209 medium.Resolve(results);
210 },
211 error => {
212 if (Interlocked.Increment(ref errors) == 1)
213 medium.Reject(
214 new Exception("The dependency promise is failed", error)
215 );
216 },
217 reason => {
218 if (Interlocked.Increment(ref errors) == 1)
219 medium.Cancel(
220 new Exception("The dependency promise is cancelled", reason)
221 );
222 }
223 );
224 i++;
225 }
226
227 return medium;
228 } 86 }
229 87
230 public static IPromise Then(this IPromise that, Action success, Action<Exception> error, Action<Exception> cancel) { 88 public static IPromise<Tout> Then<Tin, Tout>(this IPromise<Tin> that, Func<Tin, Tout> fulfilled, Func<Exception, IPromise<Tout>> rejected) {
231 Safe.ArgumentNotNull(that, "that"); 89 var reaction = new PromiseFuncReaction<Tin, Tout>(fulfilled, rejected, Promise.DefaultDispatcher);
232 90 that.Then(reaction);
233 var d = new ActionTask(success, error, cancel, false); 91 return reaction.Promise;
234 that.On(d.Resolve, d.Reject, d.CancelOperation);
235 d.CancellationRequested(that.Cancel);
236 return d;
237 } 92 }
238 93
239 public static IPromise Then(this IPromise that, Action success, Action<Exception> error) { 94 public static IPromise<Tout> Then<Tin, Tout>(this IPromise<Tin> that, Func<Tin, IPromise<Tout>> fulfilled, Func<Exception, Tout> rejected) {
240 return Then(that, success, error, null); 95 var reaction = new PromiseFuncReaction<Tin, Tout>(fulfilled, rejected, Promise.DefaultDispatcher);
96 that.Then(reaction);
97 return reaction.Promise;
241 } 98 }
242 99
243 public static IPromise Then(this IPromise that, Action success) { 100 public static IPromise<Tout> Then<Tin, Tout>(this IPromise<Tin> that, Func<Tin, IPromise<Tout>> fulfilled, Func<Exception, IPromise<Tout>> rejected) {
244 return Then(that, success, null, null); 101 var reaction = new PromiseFuncReaction<Tin, Tout>(fulfilled, rejected, Promise.DefaultDispatcher);
102 that.Then(reaction);
103 return reaction.Promise;
245 } 104 }
246 105
247 public static IPromise<T> Then<T>(this IPromise that, Func<T> success, Func<Exception, T> error, Func<Exception, T> cancel) { 106 public static IPromise Catch(this IPromise that, Action<Exception> rejected) {
248 Safe.ArgumentNotNull(that, "that"); 107 return Then(that, null, rejected);
249
250 var d = new FuncTask<T>(success, error, cancel, false);
251 that.On(d.Resolve, d.Reject, d.CancelOperation);
252 d.CancellationRequested(that.Cancel);
253 return d;
254 } 108 }
255 109
256 public static IPromise<T> Then<T>(this IPromise that, Func<T> success, Func<Exception, T> error) { 110 public static IPromise Catch(this IPromise that, Func<Exception, IPromise> rejected) {
257 return Then(that, success, error, null); 111 return Then(that, null, rejected);
258 } 112 }
259 113
260 public static IPromise<T> Then<T>(this IPromise that, Func<T> success) { 114 public static IPromise<Tout> Catch<Tout>(this IPromise that, Func<Exception, Tout> rejected) {
261 return Then(that, success, null, null); 115 return Then(that, (Func<Tout>)null, rejected);
262 } 116 }
263 117
264 public static IPromise<T2> Then<T, T2>(this IPromise<T> that, Func<T, T2> success, Func<Exception, T2> error, Func<Exception, T2> cancel) { 118 public static IPromise<Tout> Catch<Tout>(this IPromise that, Func<Exception, IPromise<Tout>> rejected) {
265 Safe.ArgumentNotNull(that, "that"); 119 return Then(that, (Func<Tout>)null, rejected);
266 Safe.ArgumentNotNull(success, "success");
267
268 var d = new FuncTask<T, T2>(success, error, cancel, false);
269 that.On(d.Resolve, d.Reject, d.CancelOperation);
270 d.CancellationRequested(that.Cancel);
271 return d;
272 } 120 }
273 121
274 public static IPromise<T> Then<T>(this IPromise<T> that, Action<T> success, Func<Exception, T> error, Func<Exception, T> cancel) { 122 public static IPromise<Tout> Catch<Tin, Tout>(this IPromise<Tin> that, Func<Exception, Tout> rejected) {
275 Safe.ArgumentNotNull(that, "that"); 123 return Then(that, (Func<Tin, Tout>)null, rejected);
276 var d = new FuncTask<T, T>(
277 x => {
278 success(x);
279 return x;
280 },
281 error,
282 cancel,
283 false
284 );
285 that.On(d.Resolve, d.Reject, d.CancelOperation);
286 d.CancellationRequested(that.Cancel);
287 return d;
288 } 124 }
289 125
290 public static IPromise<T> Then<T>(this IPromise<T> that, Action<T> success, Func<Exception, T> error) { 126 public static IPromise<Tout> Catch<Tin, Tout>(this IPromise<Tin> that, Func<Exception, IPromise<Tout>> rejected) {
291 return Then(that, success, error, null); 127 return Then(that, (Func<Tin, Tout>)null, rejected);
292 } 128 }
293
294 public static IPromise<T> Then<T>(this IPromise<T> that, Action<T> success) {
295 return Then(that, success, null, null);
296 }
297
298 public static IPromise<T2> Then<T, T2>(this IPromise<T> that, Func<T, T2> success, Func<Exception, T2> error) {
299 return Then(that, success, error, null);
300 }
301
302 public static IPromise<T2> Then<T, T2>(this IPromise<T> that, Func<T, T2> success) {
303 return Then(that, success, null, null);
304 }
305
306 public static IPromise<T> Always<T>(this IPromise<T> that, Action handler) {
307 Func<Exception, T> errorOrCancel;
308 if (handler != null)
309 errorOrCancel = e => {
310 handler();
311 throw new PromiseTransientException(e);
312 };
313 else
314 errorOrCancel = null;
315
316 return Then(
317 that,
318 x => {
319 handler();
320 return x;
321 },
322 errorOrCancel,
323 errorOrCancel);
324 }
325
326 public static IPromise Always(this IPromise that, Action handler) {
327 Action<Exception> errorOrCancel;
328 if (handler != null)
329 errorOrCancel = e => {
330 handler();
331 throw new PromiseTransientException(e);
332 };
333 else
334 errorOrCancel = null;
335
336 return Then(
337 that,
338 handler,
339 errorOrCancel,
340 errorOrCancel);
341 }
342
343 public static IPromise Error(this IPromise that, Action<Exception> handler, bool handleCancellation) {
344 Action<Exception> errorOrCancel;
345 if (handler != null)
346 errorOrCancel = e => {
347 handler(e);
348 throw new PromiseTransientException(e);
349 };
350 else
351 errorOrCancel = null;
352
353 return Then(that, null, errorOrCancel, handleCancellation ? errorOrCancel : null);
354 }
355
356 public static IPromise Error(this IPromise that, Action<Exception> handler) {
357 return Error(that, handler, false);
358 }
359
360 public static IPromise<T> Error<T>(this IPromise<T> that, Action<Exception> handler, bool handleCancellation) {
361 Func<Exception, T> errorOrCancel;
362 if (handler != null)
363 errorOrCancel = e => {
364 handler(e);
365 throw new PromiseTransientException(e);
366 };
367 else
368 errorOrCancel = null;
369
370 return Then(that, null, errorOrCancel, handleCancellation ? errorOrCancel : null);
371 }
372
373 public static IPromise<T> Error<T>(this IPromise<T> that, Action<Exception> handler) {
374 return Error(that, handler, false);
375 }
376
377 #region chain traits
378 public static IPromise Chain(this IPromise that, Func<IPromise> success, Func<Exception, IPromise> error, Func<Exception, IPromise> cancel) {
379 Safe.ArgumentNotNull(that, "that");
380
381 var d = new ActionChainTask(success, error, cancel, false);
382 that.On(d.Resolve, d.Reject, d.CancelOperation);
383 d.CancellationRequested(that.Cancel);
384 return d;
385 }
386
387 public static IPromise Chain(this IPromise that, Func<IPromise> success, Func<Exception, IPromise> error) {
388 return Chain(that, success, error, null);
389 }
390
391 public static IPromise Chain(this IPromise that, Func<IPromise> success) {
392 return Chain(that, success, null, null);
393 }
394
395 public static IPromise<T> Chain<T>(this IPromise that, Func<IPromise<T>> success, Func<Exception, IPromise<T>> error, Func<Exception, IPromise<T>> cancel) {
396 Safe.ArgumentNotNull(that, "that");
397
398 var d = new FuncChainTask<T>(success, error, cancel, false);
399 that.On(d.Resolve, d.Reject, d.CancelOperation);
400 if (success != null)
401 d.CancellationRequested(that.Cancel);
402 return d;
403 }
404
405 public static IPromise<T> Chain<T>(this IPromise that, Func<IPromise<T>> success, Func<Exception, IPromise<T>> error) {
406 return Chain(that, success, error, null);
407 }
408
409 public static IPromise<T> Chain<T>(this IPromise that, Func<IPromise<T>> success) {
410 return Chain(that, success, null, null);
411 }
412
413 public static IPromise<T2> Chain<T, T2>(this IPromise<T> that, Func<T, IPromise<T2>> success, Func<Exception, IPromise<T2>> error, Func<Exception, IPromise<T2>> cancel) {
414 Safe.ArgumentNotNull(that, "that");
415 var d = new FuncChainTask<T, T2>(success, error, cancel, false);
416 that.On(d.Resolve, d.Reject, d.CancelOperation);
417 if (success != null)
418 d.CancellationRequested(that.Cancel);
419 return d;
420 }
421
422 public static IPromise<T2> Chain<T, T2>(this IPromise<T> that, Func<T, IPromise<T2>> success, Func<Exception, IPromise<T2>> error) {
423 return Chain(that, success, error, null);
424 }
425
426 public static IPromise<T2> Chain<T, T2>(this IPromise<T> that, Func<T, IPromise<T2>> success) {
427 return Chain(that, success, null, null);
428 }
429
430 #endregion
431
432 public static IPromise<T2> Guard<T, T2>(this IPromise<T> that, Func<IPromise<T>, IPromise<T2>> continuation, Action<T> cleanup) {
433 Safe.ArgumentNotNull(that, "that");
434 Safe.ArgumentNotNull(continuation, "continuation");
435 return continuation(that).Error((err) => {
436 that.On(cleanup);
437 }, true);
438 }
439
440 #if NET_4_5
441
442 public static PromiseAwaiter<T> GetAwaiter<T>(this IPromise<T> that) {
443 Safe.ArgumentNotNull(that, "that");
444
445 return new PromiseAwaiter<T>(that);
446 }
447
448 public static PromiseAwaiter GetAwaiter(this IPromise that) {
449 Safe.ArgumentNotNull(that, "that");
450
451 return new PromiseAwaiter(that);
452 }
453
454 public static IPromise BoundCancellationToken(this IPromise that, CancellationToken ct) {
455 Safe.ArgumentNotNull(that, "that");
456 ct.Register(that.Cancel);
457 return that.Then(null, null, (err) => {
458 ct.ThrowIfCancellationRequested();
459 throw new PromiseTransientException(err);
460 });
461 }
462
463 public static IPromise<T> BoundCancellationToken<T>(this IPromise<T> that, CancellationToken ct) {
464 Safe.ArgumentNotNull(that, "that");
465 ct.Register(that.Cancel);
466 return that.Then(null, null, (err) => {
467 ct.ThrowIfCancellationRequested();
468 throw new PromiseTransientException(err);
469 });
470 }
471
472 #endif
473 } 129 }
474 } 130 }
475 131