| @@ -17,71 +17,25 namespace Implab.Fx | |||||
| 17 | /// <example> |
|
17 | /// <example> | |
| 18 | /// client |
|
18 | /// client | |
| 19 | /// .Get("description.txt") // returns a promise |
|
19 | /// .Get("description.txt") // returns a promise | |
| 20 |
/// .Di |
|
20 | /// .DispatchToControl(m_ctl) // handle the promise in the thread of the control | |
| 21 | /// .Then( |
|
21 | /// .Then( | |
| 22 | /// description => m_ctl.Text = description // now it's safe |
|
22 | /// description => m_ctl.Text = description // now it's safe | |
| 23 | /// ) |
|
23 | /// ) | |
| 24 | /// </example> |
|
24 | /// </example> | |
| 25 | public static Promise<T> DispatchToControl<T>(this Promise<T> that, Control ctl) |
|
25 | public static IPromise<T> DispatchToControl<T>(this IPromise<T> that, Control ctl) | |
| 26 | { |
|
26 | { | |
| 27 | if (that == null) |
|
27 | Safe.ArgumentNotNull(that, "that"); | |
| 28 |
|
|
28 | Safe.ArgumentNotNull(ctl, "ctl"); | |
| 29 | if (ctl == null) |
|
|||
| 30 | throw new ArgumentNullException("ctl"); |
|
|||
| 31 |
|
29 | |||
| 32 | var directed = new ControlBoundPromise<T>(ctl,that,true); |
|
30 | var directed = new ControlBoundPromise<T>(ctl,that,true); | |
| 33 |
|
31 | |||
| 34 |
that. |
|
32 | that.Last( | |
| 35 | directed.Resolve, |
|
33 | directed.Resolve, | |
| 36 |
|
|
34 | directed.Reject, | |
| 37 |
|
|
35 | directed.Cancel | |
| 38 | directed.Reject(err); |
|
|||
| 39 | return default(T); |
|
|||
| 40 | } |
|
|||
| 41 | ); |
|
36 | ); | |
| 42 |
|
37 | |||
| 43 | return directed; |
|
38 | return directed; | |
| 44 | } |
|
39 | } | |
| 45 |
|
||||
| 46 | /// <summary> |
|
|||
| 47 | /// Направляет обработку обещания в текущий поток, если у него существует контекст синхронизации. |
|
|||
| 48 | /// </summary> |
|
|||
| 49 | /// <typeparam name="T">Тип результата обещания.</typeparam> |
|
|||
| 50 | /// <param name="that">Обещание которое нужно обработать в текущем потоке.</param> |
|
|||
| 51 | /// <returns>Перенаправленное обещание.</returns> |
|
|||
| 52 | public static Promise<T> DispatchToCurrentThread<T>(this Promise<T> that) |
|
|||
| 53 | { |
|
|||
| 54 | var sync = SynchronizationContext.Current; |
|
|||
| 55 | if (sync == null) |
|
|||
| 56 | throw new InvalidOperationException("The current thread doesn't have a syncronization context"); |
|
|||
| 57 | return DispatchToSyncContext(that, sync); |
|
|||
| 58 | } |
|
|||
| 59 |
|
||||
| 60 | /// <summary> |
|
|||
| 61 | /// Направляет обработку обещания в указанный контекст синхронизации. |
|
|||
| 62 | /// </summary> |
|
|||
| 63 | /// <typeparam name="T">Тип результата обещания.</typeparam> |
|
|||
| 64 | /// <param name="that">Обещание, которое требуется обработать в указанном контексте синхронизации.</param> |
|
|||
| 65 | /// <param name="sync">Контекст синхронизации в который будет направлено обещание.</param> |
|
|||
| 66 | /// <returns>Новое обещание, которое будет обрабатываться в указанном контексте.</returns> |
|
|||
| 67 | public static Promise<T> DispatchToSyncContext<T>(this Promise<T> that, SynchronizationContext sync) |
|
|||
| 68 | { |
|
|||
| 69 | if (that == null) |
|
|||
| 70 | throw new ArgumentNullException("that"); |
|
|||
| 71 | if (sync == null) |
|
|||
| 72 | throw new ArgumentNullException("sync"); |
|
|||
| 73 |
|
||||
| 74 | var d = new Promise<T>(); |
|
|||
| 75 |
|
||||
| 76 | that.Then( |
|
|||
| 77 | res => sync.Post(state => d.Resolve(res), null), |
|
|||
| 78 | err => { |
|
|||
| 79 | sync.Post(state => d.Reject(err), null); |
|
|||
| 80 | return default(T); |
|
|||
| 81 | } |
|
|||
| 82 | ); |
|
|||
| 83 |
|
||||
| 84 | return d; |
|
|||
| 85 | } |
|
|||
| 86 | } |
|
40 | } | |
| 87 | } |
|
41 | } | |
| @@ -32,5 +32,4 using System.Runtime.InteropServices; | |||||
| 32 | // You can specify all the values or you can default the Build and Revision Numbers |
|
32 | // You can specify all the values or you can default the Build and Revision Numbers | |
| 33 | // by using the '*' as shown below: |
|
33 | // by using the '*' as shown below: | |
| 34 | // [assembly: AssemblyVersion("1.0.*")] |
|
34 | // [assembly: AssemblyVersion("1.0.*")] | |
| 35 |
[assembly: AssemblyVersion(" |
|
35 | [assembly: AssemblyVersion("2.0.*")] | |
| 36 | [assembly: AssemblyFileVersion("1.0.0.0")] |
|
|||
| @@ -38,6 +38,38 namespace Implab.Test { | |||||
| 38 | } |
|
38 | } | |
| 39 |
|
39 | |||
| 40 | [TestMethod] |
|
40 | [TestMethod] | |
|
|
41 | public void CancelExceptionTest() { | |||
|
|
42 | var p = new Promise<bool>(); | |||
|
|
43 | p.Cancel(); | |||
|
|
44 | ||||
|
|
45 | var p2 = p.Cancelled(() => { | |||
|
|
46 | throw new ApplicationException("CANCELLED"); | |||
|
|
47 | }); | |||
|
|
48 | ||||
|
|
49 | try { | |||
|
|
50 | p2.Join(); | |||
|
|
51 | Assert.Fail(); | |||
|
|
52 | } catch (ApplicationException err) { | |||
|
|
53 | Assert.AreEqual("CANCELLED", err.InnerException.Message); | |||
|
|
54 | } | |||
|
|
55 | ||||
|
|
56 | } | |||
|
|
57 | ||||
|
|
58 | [TestMethod] | |||
|
|
59 | public void ContinueOnCancelTest() { | |||
|
|
60 | var p = new Promise<bool>(); | |||
|
|
61 | p.Cancel(); | |||
|
|
62 | ||||
|
|
63 | var p2 = p | |||
|
|
64 | .Cancelled(() => { | |||
|
|
65 | throw new ApplicationException("CANCELLED"); | |||
|
|
66 | }) | |||
|
|
67 | .Error(e => true); | |||
|
|
68 | ||||
|
|
69 | Assert.AreEqual(true, p2.Join()); | |||
|
|
70 | } | |||
|
|
71 | ||||
|
|
72 | [TestMethod] | |||
| 41 | public void JoinSuccessTest() { |
|
73 | public void JoinSuccessTest() { | |
| 42 | var p = new Promise<int>(); |
|
74 | var p = new Promise<int>(); | |
| 43 | p.Resolve(100); |
|
75 | p.Resolve(100); | |
| @@ -63,7 +95,7 namespace Implab.Test { | |||||
| 63 | public void MapTest() { |
|
95 | public void MapTest() { | |
| 64 | var p = new Promise<int>(); |
|
96 | var p = new Promise<int>(); | |
| 65 |
|
97 | |||
| 66 |
var p2 = p. |
|
98 | var p2 = p.Then(x => x.ToString()); | |
| 67 | p.Resolve(100); |
|
99 | p.Resolve(100); | |
| 68 |
|
100 | |||
| 69 | Assert.AreEqual(p2.Join(), "100"); |
|
101 | Assert.AreEqual(p2.Join(), "100"); | |
| @@ -185,8 +217,8 namespace Implab.Test { | |||||
| 185 | var stop = new ManualResetEvent(false); |
|
217 | var stop = new ManualResetEvent(false); | |
| 186 | int total = 0; |
|
218 | int total = 0; | |
| 187 |
|
219 | |||
| 188 | int itemsPerWriter = 1000; |
|
220 | int itemsPerWriter = 10000; | |
| 189 |
int writersCount = |
|
221 | int writersCount = 10; | |
| 190 |
|
222 | |||
| 191 | for (int i = 0; i < writersCount; i++) { |
|
223 | for (int i = 0; i < writersCount; i++) { | |
| 192 | Interlocked.Increment(ref writers); |
|
224 | Interlocked.Increment(ref writers); | |
| @@ -318,7 +350,7 namespace Implab.Test { | |||||
| 318 | .Chain(x => |
|
350 | .Chain(x => | |
| 319 | PromiseHelper |
|
351 | PromiseHelper | |
| 320 | .Sleep(200, "Hi, " + x) |
|
352 | .Sleep(200, "Hi, " + x) | |
| 321 |
. |
|
353 | .Then(y => y) | |
| 322 | .Cancelled(() => flags[1] = true) |
|
354 | .Cancelled(() => flags[1] = true) | |
| 323 | ) |
|
355 | ) | |
| 324 | .Cancelled(() => flags[2] = true); |
|
356 | .Cancelled(() => flags[2] = true); | |
| @@ -341,7 +373,7 namespace Implab.Test { | |||||
| 341 | // завершаться ошибкой OperationCanceledException |
|
373 | // завершаться ошибкой OperationCanceledException | |
| 342 | var p = PromiseHelper |
|
374 | var p = PromiseHelper | |
| 343 | .Sleep(1, "Hi, HAL!") |
|
375 | .Sleep(1, "Hi, HAL!") | |
| 344 |
. |
|
376 | .Then(x => { | |
| 345 | // запускаем две асинхронные операции |
|
377 | // запускаем две асинхронные операции | |
| 346 | var result = PromiseHelper.Sleep(1000, "HEM ENABLED!!!"); |
|
378 | var result = PromiseHelper.Sleep(1000, "HEM ENABLED!!!"); | |
| 347 | // вторая операция отменяет первую до завершения |
|
379 | // вторая операция отменяет первую до завершения | |
| @@ -360,16 +392,15 namespace Implab.Test { | |||||
| 360 | [TestMethod] |
|
392 | [TestMethod] | |
| 361 | public void ChainedCancel2Test() { |
|
393 | public void ChainedCancel2Test() { | |
| 362 | // при отмене цепочки обещаний, вложенные операции также должны отменяться |
|
394 | // при отмене цепочки обещаний, вложенные операции также должны отменяться | |
| 363 | IPromise p = null; |
|
|||
| 364 | var pSurvive = new Promise<bool>(); |
|
395 | var pSurvive = new Promise<bool>(); | |
| 365 | var hemStarted = new ManualResetEvent(false); |
|
396 | var hemStarted = new ManualResetEvent(false); | |
| 366 | p = PromiseHelper |
|
397 | var p = PromiseHelper | |
| 367 | .Sleep(1, "Hi, HAL!") |
|
398 | .Sleep(1, "Hi, HAL!") | |
| 368 | .Chain(x => { |
|
399 | .Chain(x => { | |
| 369 | hemStarted.Set(); |
|
400 | hemStarted.Set(); | |
| 370 | // запускаем две асинхронные операции |
|
401 | // запускаем две асинхронные операции | |
| 371 | var result = PromiseHelper |
|
402 | var result = PromiseHelper | |
| 372 | .Sleep(1000, "HEM ENABLED!!!") |
|
403 | .Sleep(10000, "HEM ENABLED!!!") | |
| 373 | .Then(s => pSurvive.Resolve(false)); |
|
404 | .Then(s => pSurvive.Resolve(false)); | |
| 374 |
|
405 | |||
| 375 | result |
|
406 | result | |
| @@ -215,7 +215,7 namespace Implab.Diagnostics { | |||||
| 215 | Safe.ArgumentNotNull(promise, "promise"); |
|
215 | Safe.ArgumentNotNull(promise, "promise"); | |
| 216 |
|
216 | |||
| 217 | var ctx = DetachLogicalOperation(); |
|
217 | var ctx = DetachLogicalOperation(); | |
| 218 |
promise. |
|
218 | promise.Anyway(() => { | |
| 219 | var old = _current; |
|
219 | var old = _current; | |
| 220 | TraceContext.Attach(ctx); |
|
220 | TraceContext.Attach(ctx); | |
| 221 | TraceContext.Current.EndLogicalOperation(); |
|
221 | TraceContext.Current.EndLogicalOperation(); | |
| @@ -5,6 +5,6 using System.Text; | |||||
| 5 |
|
5 | |||
| 6 | namespace Implab { |
|
6 | namespace Implab { | |
| 7 | public interface ICancellable { |
|
7 | public interface ICancellable { | |
| 8 |
|
|
8 | void Cancel(); | |
| 9 | } |
|
9 | } | |
| 10 | } |
|
10 | } | |
| @@ -52,7 +52,7 namespace Implab { | |||||
| 52 | /// </summary> |
|
52 | /// </summary> | |
| 53 | /// <param name="handler">Обработчик.</param> |
|
53 | /// <param name="handler">Обработчик.</param> | |
| 54 | /// <remarks>После обработке ошибки, она передается дальше.</remarks> |
|
54 | /// <remarks>После обработке ошибки, она передается дальше.</remarks> | |
| 55 |
IPromise |
|
55 | IPromise Anyway(Action handler); | |
| 56 | /// <summary> |
|
56 | /// <summary> | |
| 57 | /// Обработчик для регистрации отмены обещания, событие отмены не может быть подавлено. |
|
57 | /// Обработчик для регистрации отмены обещания, событие отмены не может быть подавлено. | |
| 58 | /// </summary> |
|
58 | /// </summary> | |
| @@ -4,34 +4,40 using System.Linq; | |||||
| 4 | using System.Text; |
|
4 | using System.Text; | |
| 5 |
|
5 | |||
| 6 | namespace Implab { |
|
6 | namespace Implab { | |
| 7 | public interface IPromise<T>: IPromise { |
|
7 | public interface IPromise<T> : IPromise { | |
| 8 |
|
8 | |||
| 9 | new T Join(); |
|
9 | new T Join(); | |
| 10 |
|
10 | |||
| 11 | new T Join(int timeout); |
|
11 | new T Join(int timeout); | |
| 12 |
|
12 | |||
|
|
13 | void Last(ResultHandler<T> success, ErrorHandler error, Action cancel); | |||
|
|
14 | ||||
|
|
15 | void Last(ResultHandler<T> success, ErrorHandler error); | |||
|
|
16 | ||||
|
|
17 | void Last(ResultHandler<T> success); | |||
|
|
18 | ||||
| 13 | IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error, Action cancel); |
|
19 | IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error, Action cancel); | |
| 14 |
|
20 | |||
| 15 | IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error); |
|
21 | IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error); | |
| 16 |
|
22 | |||
| 17 | IPromise<T> Then(ResultHandler<T> success); |
|
23 | IPromise<T> Then(ResultHandler<T> success); | |
| 18 |
|
24 | |||
| 19 |
|
|
25 | IPromise<T2> Then<T2>(ResultMapper<T, T2> mapper, ErrorHandler<T2> error, Action cancel); | |
| 20 | void Last(ResultHandler<T> success, ErrorHandler error); |
|
26 | ||
| 21 | void Last(ResultHandler<T> success); |
|
27 | IPromise<T2> Then<T2>(ResultMapper<T, T2> mapper, ErrorHandler<T2> error); | |
|
|
28 | ||||
|
|
29 | IPromise<T2> Then<T2>(ResultMapper<T, T2> mapper); | |||
|
|
30 | ||||
|
|
31 | IPromise<T2> Chain<T2>(ResultMapper<T, IPromise<T2>> chained, ErrorHandler<IPromise<T2>> error, Action cancel); | |||
|
|
32 | ||||
|
|
33 | IPromise<T2> Chain<T2>(ResultMapper<T, IPromise<T2>> chained, ErrorHandler<IPromise<T2>> error); | |||
|
|
34 | ||||
|
|
35 | IPromise<T2> Chain<T2>(ResultMapper<T, IPromise<T2>> chained); | |||
| 22 |
|
36 | |||
| 23 | IPromise<T> Error(ErrorHandler<T> error); |
|
37 | IPromise<T> Error(ErrorHandler<T> error); | |
| 24 |
|
38 | |||
| 25 | IPromise<T2> Then<T2>(ResultMapper<T,T2> mapper, ErrorHandler<T> error); |
|
|||
| 26 |
|
||||
| 27 | IPromise<T2> Then<T2>(ResultMapper<T,T2> mapper); |
|
|||
| 28 |
|
||||
| 29 | IPromise<T2> Then<T2>(ChainedOperation<T, T2> chained, ErrorHandler<T> error); |
|
|||
| 30 |
|
||||
| 31 | IPromise<T2> Then<T2>(ChainedOperation<T, T2> chained); |
|
|||
| 32 |
|
||||
| 33 | new IPromise<T> Cancelled(Action handler); |
|
39 | new IPromise<T> Cancelled(Action handler); | |
| 34 |
|
40 | |||
| 35 |
new IPromise<T> |
|
41 | new IPromise<T> Anyway(Action handler); | |
| 36 | } |
|
42 | } | |
| 37 | } |
|
43 | } | |
| @@ -29,7 +29,7 namespace Implab.Parallels { | |||||
| 29 | m_pending = source.Length; |
|
29 | m_pending = source.Length; | |
| 30 | m_action = action; |
|
30 | m_action = action; | |
| 31 |
|
31 | |||
| 32 |
m_promise. |
|
32 | m_promise.Anyway(Dispose); | |
| 33 |
|
33 | |||
| 34 | InitPool(); |
|
34 | InitPool(); | |
| 35 | } |
|
35 | } | |
| @@ -85,7 +85,7 namespace Implab.Parallels { | |||||
| 85 | m_transform = transform; |
|
85 | m_transform = transform; | |
| 86 | m_traceContext = TraceContext.Snapshot(); |
|
86 | m_traceContext = TraceContext.Snapshot(); | |
| 87 |
|
87 | |||
| 88 |
m_promise. |
|
88 | m_promise.Anyway(Dispose); | |
| 89 |
|
89 | |||
| 90 | InitPool(); |
|
90 | InitPool(); | |
| 91 | } |
|
91 | } | |
| @@ -138,7 +138,7 namespace Implab.Parallels { | |||||
| 138 | return iter.Promise; |
|
138 | return iter.Promise; | |
| 139 | } |
|
139 | } | |
| 140 |
|
140 | |||
| 141 |
public static IPromise<TDst[]> ChainedMap<TSrc, TDst>(this TSrc[] source, |
|
141 | public static IPromise<TDst[]> ChainedMap<TSrc, TDst>(this TSrc[] source, ResultMapper<TSrc, IPromise<TDst>> transform, int threads) { | |
| 142 | if (source == null) |
|
142 | if (source == null) | |
| 143 | throw new ArgumentNullException("source"); |
|
143 | throw new ArgumentNullException("source"); | |
| 144 | if (transform == null) |
|
144 | if (transform == null) | |
| @@ -165,7 +165,7 namespace Implab.Parallels { | |||||
| 165 | semaphore.WaitOne(); |
|
165 | semaphore.WaitOne(); | |
| 166 | try { |
|
166 | try { | |
| 167 | var p1 = transform(source[i]); |
|
167 | var p1 = transform(source[i]); | |
| 168 |
p1. |
|
168 | p1.Anyway(() => semaphore.Release()); | |
| 169 | p1.Then( |
|
169 | p1.Then( | |
| 170 | x => { |
|
170 | x => { | |
| 171 | res[idx] = x; |
|
171 | res[idx] = x; | |
| @@ -186,7 +186,7 namespace Implab.Parallels { | |||||
| 186 | return 0; |
|
186 | return 0; | |
| 187 | }); |
|
187 | }); | |
| 188 |
|
188 | |||
| 189 |
return promise. |
|
189 | return promise.Anyway(semaphore.Dispose); | |
| 190 | } |
|
190 | } | |
| 191 |
|
191 | |||
| 192 | } |
|
192 | } | |
| @@ -11,7 +11,6 namespace Implab { | |||||
| 11 | public delegate T ErrorHandler<out T>(Exception e); |
|
11 | public delegate T ErrorHandler<out T>(Exception e); | |
| 12 | public delegate void ResultHandler<in T>(T result); |
|
12 | public delegate void ResultHandler<in T>(T result); | |
| 13 | public delegate TNew ResultMapper<in TSrc,out TNew>(TSrc result); |
|
13 | public delegate TNew ResultMapper<in TSrc,out TNew>(TSrc result); | |
| 14 | public delegate IPromise<TNew> ChainedOperation<in TSrc,TNew>(TSrc result); |
|
|||
| 15 |
|
14 | |||
| 16 | /// <summary> |
|
15 | /// <summary> | |
| 17 | /// Класс для асинхронного получения результатов. Так называемое "обещание". |
|
16 | /// Класс для асинхронного получения результатов. Так называемое "обещание". | |
| @@ -121,10 +120,15 namespace Implab { | |||||
| 121 | public Promise(IPromise parent, bool cancellable) { |
|
120 | public Promise(IPromise parent, bool cancellable) { | |
| 122 | m_cancellable = cancellable; |
|
121 | m_cancellable = cancellable; | |
| 123 | if (parent != null) |
|
122 | if (parent != null) | |
| 124 |
|
|
123 | AddHandler( | |
| 125 |
|
|
124 | null, | |
| 126 |
|
|
125 | null, | |
| 127 |
|
|
126 | () => { | |
|
|
127 | if (parent.IsExclusive) | |||
|
|
128 | parent.Cancel(); | |||
|
|
129 | }, | |||
|
|
130 | null | |||
|
|
131 | ); | |||
| 128 | } |
|
132 | } | |
| 129 |
|
133 | |||
| 130 | bool BeginTransit() { |
|
134 | bool BeginTransit() { | |
| @@ -210,22 +214,14 namespace Implab { | |||||
| 210 | /// <summary> |
|
214 | /// <summary> | |
| 211 | /// Отменяет операцию, если это возможно. |
|
215 | /// Отменяет операцию, если это возможно. | |
| 212 | /// </summary> |
|
216 | /// </summary> | |
| 213 | /// <returns><c>true</c> Операция была отменена, обработчики не будут вызваны.<c>false</c> отмена не возможна, поскольку обещание уже выполнено и обработчики отработали.</returns> |
|
217 | /// <remarks>Для определения была ли операция отменена следует использовать свойство <see cref="IsCancelled"/>.</remarks> | |
| 214 |
public |
|
218 | public void Cancel() { | |
| 215 | if (m_cancellable && BeginTransit()) { |
|
219 | if (m_cancellable && BeginTransit()) { | |
| 216 | CompleteTransit(CANCELLED_STATE); |
|
220 | CompleteTransit(CANCELLED_STATE); | |
| 217 | OnStateChanged(); |
|
221 | OnStateChanged(); | |
| 218 | return true; |
|
|||
| 219 | } |
|
222 | } | |
| 220 | return false; |
|
|||
| 221 | } |
|
223 | } | |
| 222 |
|
224 | |||
| 223 | // сделано для возвращаемого типа void |
|
|||
| 224 | protected void InternalCancel() { |
|
|||
| 225 | Cancel(); |
|
|||
| 226 | } |
|
|||
| 227 |
|
||||
| 228 |
|
||||
| 229 | public IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error, Action cancel) { |
|
225 | public IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error, Action cancel) { | |
| 230 | if (success == null && error == null && cancel == null) |
|
226 | if (success == null && error == null && cancel == null) | |
| 231 | return this; |
|
227 | return this; | |
| @@ -255,30 +251,7 namespace Implab { | |||||
| 255 | return medium; |
|
251 | return medium; | |
| 256 | } |
|
252 | } | |
| 257 |
|
253 | |||
| 258 | public IPromise Then(Action success, ErrorHandler error, Action cancel) { |
|
254 | ||
| 259 | return Then( |
|
|||
| 260 | x => success(), |
|
|||
| 261 | e => { |
|
|||
| 262 | error(e); |
|
|||
| 263 | return default(T); |
|
|||
| 264 | }, |
|
|||
| 265 | cancel |
|
|||
| 266 | ); |
|
|||
| 267 | } |
|
|||
| 268 |
|
||||
| 269 | public IPromise Then(Action success, ErrorHandler error) { |
|
|||
| 270 | return Then( |
|
|||
| 271 | x => success(), |
|
|||
| 272 | e => { |
|
|||
| 273 | error(e); |
|
|||
| 274 | return default(T); |
|
|||
| 275 | } |
|
|||
| 276 | ); |
|
|||
| 277 | } |
|
|||
| 278 |
|
||||
| 279 | public IPromise Then(Action success) { |
|
|||
| 280 | return Then(x => success()); |
|
|||
| 281 | } |
|
|||
| 282 |
|
255 | |||
| 283 |
|
256 | |||
| 284 | public IPromise<T> Then(ResultHandler<T> success) { |
|
257 | public IPromise<T> Then(ResultHandler<T> success) { | |
| @@ -292,6 +265,23 namespace Implab { | |||||
| 292 | return medium; |
|
265 | return medium; | |
| 293 | } |
|
266 | } | |
| 294 |
|
267 | |||
|
|
268 | /// <summary> | |||
|
|
269 | /// Последний обработчик в цепочки обещаний. | |||
|
|
270 | /// </summary> | |||
|
|
271 | /// <param name="success"></param> | |||
|
|
272 | /// <param name="error"></param> | |||
|
|
273 | /// <param name="cancel"></param> | |||
|
|
274 | /// <remarks> | |||
|
|
275 | /// <para> | |||
|
|
276 | /// Данный метод не создает связанного с текущим обещания и предназначен для окончания | |||
|
|
277 | /// фсинхронной цепочки. | |||
|
|
278 | /// </para> | |||
|
|
279 | /// <para> | |||
|
|
280 | /// Если данный метод вызвать несколько раз, либо добавить другие обработчики, то цепочка | |||
|
|
281 | /// не будет одиночной <see cref="IsExclusive"/> и, как следствие, будет невозможна отмена | |||
|
|
282 | /// всей цепи обещаний снизу (с самого последнего обещания). | |||
|
|
283 | /// </para> | |||
|
|
284 | /// </remarks> | |||
| 295 | public void Last(ResultHandler<T> success, ErrorHandler error, Action cancel) { |
|
285 | public void Last(ResultHandler<T> success, ErrorHandler error, Action cancel) { | |
| 296 | if (success == null && error == null && cancel == null) |
|
286 | if (success == null && error == null && cancel == null) | |
| 297 | return; |
|
287 | return; | |
| @@ -313,18 +303,6 namespace Implab { | |||||
| 313 | Last(success, null, null); |
|
303 | Last(success, null, null); | |
| 314 | } |
|
304 | } | |
| 315 |
|
305 | |||
| 316 | public void Last(Action success,ErrorHandler error, Action cancel) { |
|
|||
| 317 | Last(x => success(), error, cancel); |
|
|||
| 318 | } |
|
|||
| 319 |
|
||||
| 320 | public void Last(Action success,ErrorHandler error) { |
|
|||
| 321 | Last(x => success(), error, null); |
|
|||
| 322 | } |
|
|||
| 323 |
|
||||
| 324 | public void Last(Action success) { |
|
|||
| 325 | Last(x => success(), null, null); |
|
|||
| 326 | } |
|
|||
| 327 |
|
||||
| 328 | public IPromise Error(ErrorHandler error) { |
|
306 | public IPromise Error(ErrorHandler error) { | |
| 329 | if (error == null) |
|
307 | if (error == null) | |
| 330 | return this; |
|
308 | return this; | |
| @@ -371,44 +349,56 namespace Implab { | |||||
| 371 | /// <param name="error">Обработчик ошибки. Данный обработчик получит |
|
349 | /// <param name="error">Обработчик ошибки. Данный обработчик получит | |
| 372 | /// исключение возникшее при выполнении операции.</param> |
|
350 | /// исключение возникшее при выполнении операции.</param> | |
| 373 | /// <returns>Новое обещание, которое будет выполнено при выполнении исходного обещания.</returns> |
|
351 | /// <returns>Новое обещание, которое будет выполнено при выполнении исходного обещания.</returns> | |
| 374 | public IPromise<TNew> Then<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler<T> error) { |
|
352 | public IPromise<TNew> Then<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler<TNew> error, Action cancel) { | |
| 375 | if (mapper == null) |
|
353 | Safe.ArgumentNotNull(mapper, "mapper"); | |
| 376 | throw new ArgumentNullException("mapper"); |
|
354 | ||
|
|
355 | // создаем прицепленное обещание | |||
|
|
356 | var medium = new Promise<TNew>(this, true); | |||
| 377 |
|
357 | |||
| 378 | // создаем прицепленное обещание |
|
358 | ResultHandler<T> resultHandler = result => medium.Resolve(mapper(result)); | |
| 379 | var chained = new Promise<TNew>(this, true); |
|
|||
| 380 |
|
||||
| 381 | ResultHandler<T> resultHandler = result => chained.Resolve(mapper(result)); |
|
|||
| 382 | ErrorHandler<T> errorHandler; |
|
359 | ErrorHandler<T> errorHandler; | |
| 383 | if (error != null) |
|
360 | if (error != null) | |
| 384 | errorHandler = e => { |
|
361 | errorHandler = e => { | |
| 385 | try { |
|
362 | try { | |
| 386 |
|
|
363 | medium.Resolve(error(e)); | |
| 387 | } catch (Exception e2) { |
|
364 | } catch (Exception e2) { | |
| 388 | // в случае ошибки нужно передать исключение дальше по цепочке |
|
365 | // в случае ошибки нужно передать исключение дальше по цепочке | |
| 389 |
|
|
366 | medium.Reject(e2); | |
| 390 | } |
|
367 | } | |
| 391 | return default(T); |
|
368 | return default(T); | |
| 392 | }; |
|
369 | }; | |
| 393 | else |
|
370 | else | |
| 394 | errorHandler = e => { |
|
371 | errorHandler = e => { | |
| 395 |
|
|
372 | medium.Reject(e); | |
| 396 | return default(T); |
|
373 | return default(T); | |
| 397 | }; |
|
374 | }; | |
| 398 |
|
375 | |||
|
|
376 | Action cancelHandler; | |||
|
|
377 | if (cancel != null) | |||
|
|
378 | cancelHandler = () => { | |||
|
|
379 | cancel(); | |||
|
|
380 | medium.Cancel(); | |||
|
|
381 | }; | |||
|
|
382 | else | |||
|
|
383 | cancelHandler = medium.Cancel; | |||
|
|
384 | ||||
| 399 |
|
385 | |||
| 400 | AddHandler( |
|
386 | AddHandler( | |
| 401 | resultHandler, |
|
387 | resultHandler, | |
| 402 | errorHandler, |
|
388 | errorHandler, | |
| 403 |
c |
|
389 | cancelHandler, | |
| 404 | null |
|
390 | null | |
| 405 | ); |
|
391 | ); | |
| 406 |
|
392 | |||
| 407 |
return |
|
393 | return medium; | |
|
|
394 | } | |||
|
|
395 | ||||
|
|
396 | public IPromise<TNew> Then<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler<TNew> error) { | |||
|
|
397 | return Then(mapper, error, null); | |||
| 408 | } |
|
398 | } | |
| 409 |
|
399 | |||
| 410 | public IPromise<TNew> Then<TNew>(ResultMapper<T, TNew> mapper) { |
|
400 | public IPromise<TNew> Then<TNew>(ResultMapper<T, TNew> mapper) { | |
| 411 | return Then(mapper, null); |
|
401 | return Then(mapper, null, null); | |
| 412 | } |
|
402 | } | |
| 413 |
|
403 | |||
| 414 | /// <summary> |
|
404 | /// <summary> | |
| @@ -421,7 +411,9 namespace Implab { | |||||
| 421 | /// <param name="error">Обработчик ошибки. Данный обработчик получит |
|
411 | /// <param name="error">Обработчик ошибки. Данный обработчик получит | |
| 422 | /// исключение возникшее при выполнении текуещй операции.</param> |
|
412 | /// исключение возникшее при выполнении текуещй операции.</param> | |
| 423 | /// <returns>Новое обещание, которое будет выполнено по окончанию указанной аснхронной операции.</returns> |
|
413 | /// <returns>Новое обещание, которое будет выполнено по окончанию указанной аснхронной операции.</returns> | |
| 424 |
public IPromise<TNew> |
|
414 | public IPromise<TNew> Chain<TNew>(ResultMapper<T, IPromise<TNew>> chained, ErrorHandler<IPromise<TNew>> error, Action cancel) { | |
|
|
415 | ||||
|
|
416 | Safe.ArgumentNotNull(chained, "chained"); | |||
| 425 |
|
417 | |||
| 426 | // проблема в том, что на момент связывания еще не начата асинхронная операция, поэтому нужно |
|
418 | // проблема в том, что на момент связывания еще не начата асинхронная операция, поэтому нужно | |
| 427 | // создать посредника, к которому будут подвызяваться следующие обработчики. |
|
419 | // создать посредника, к которому будут подвызяваться следующие обработчики. | |
| @@ -435,12 +427,10 namespace Implab { | |||||
| 435 |
|
427 | |||
| 436 | var promise = chained(result); |
|
428 | var promise = chained(result); | |
| 437 |
|
429 | |||
| 438 |
promise. |
|
430 | promise.Last( | |
| 439 | medium.Resolve, |
|
431 | medium.Resolve, | |
| 440 |
|
|
432 | medium.Reject, | |
| 441 | medium.Reject(err); |
|
433 | () => medium.Reject(new OperationCanceledException()) // внешняя отмена связанной операции рассматривается как ошибка | |
| 442 | throw new TransientPromiseException(err); |
|
|||
| 443 | } |
|
|||
| 444 | ); |
|
434 | ); | |
| 445 |
|
435 | |||
| 446 | // notify chained operation that it's not needed anymore |
|
436 | // notify chained operation that it's not needed anymore | |
| @@ -450,41 +440,70 namespace Implab { | |||||
| 450 | if (promise.IsExclusive) |
|
440 | if (promise.IsExclusive) | |
| 451 | promise.Cancel(); |
|
441 | promise.Cancel(); | |
| 452 | }); |
|
442 | }); | |
| 453 |
|
||||
| 454 | // внешняя отмена связанной операции рассматривается как ошибка |
|
|||
| 455 | promise.Cancelled(() => medium.Reject(new OperationCanceledException())); |
|
|||
| 456 | }; |
|
443 | }; | |
| 457 |
|
444 | |||
| 458 |
ErrorHandler<T> errorHandler |
|
445 | ErrorHandler<T> errorHandler; | |
| 459 | if (error != null) { |
|
446 | ||
|
|
447 | if (error != null) | |||
|
|
448 | errorHandler = delegate(Exception e) { | |||
| 460 | try { |
|
449 | try { | |
| 461 |
|
|
450 | var promise = error(e); | |
|
|
451 | ||||
|
|
452 | promise.Last( | |||
|
|
453 | medium.Resolve, | |||
|
|
454 | medium.Reject, | |||
|
|
455 | () => medium.Reject(new OperationCanceledException()) // внешняя отмена связанной операции рассматривается как ошибка | |||
|
|
456 | ); | |||
|
|
457 | ||||
|
|
458 | // notify chained operation that it's not needed anymore | |||
|
|
459 | // порядок вызова Then, Cancelled важен, поскольку от этого | |||
|
|
460 | // зависит IsExclusive | |||
|
|
461 | medium.Cancelled(() => { | |||
|
|
462 | if (promise.IsExclusive) | |||
|
|
463 | promise.Cancel(); | |||
|
|
464 | }); | |||
| 462 | } catch (Exception e2) { |
|
465 | } catch (Exception e2) { | |
| 463 | medium.Reject(e2); |
|
466 | medium.Reject(e2); | |
| 464 | return default(T); |
|
|||
| 465 | } |
|
467 | } | |
| 466 | } |
|
468 | return default(T); | |
| 467 | // в случае ошибки нужно передать исключение дальше по цепочке |
|
469 | }; | |
| 468 | medium.Reject(e); |
|
470 | else | |
| 469 | return default(T); |
|
471 | errorHandler = err => { | |
| 470 | }; |
|
472 | medium.Reject(err); | |
|
|
473 | return default(T); | |||
|
|
474 | }; | |||
|
|
475 | ||||
|
|
476 | ||||
|
|
477 | Action cancelHandler; | |||
|
|
478 | if (cancel != null) | |||
|
|
479 | cancelHandler = () => { | |||
|
|
480 | if (cancel != null) | |||
|
|
481 | cancel(); | |||
|
|
482 | medium.Cancel(); | |||
|
|
483 | }; | |||
|
|
484 | else | |||
|
|
485 | cancelHandler = medium.Cancel; | |||
| 471 |
|
486 | |||
| 472 | AddHandler( |
|
487 | AddHandler( | |
| 473 | resultHandler, |
|
488 | resultHandler, | |
| 474 | errorHandler, |
|
489 | errorHandler, | |
| 475 |
|
|
490 | cancelHandler, | |
| 476 | null |
|
491 | null | |
| 477 | ); |
|
492 | ); | |
| 478 |
|
493 | |||
| 479 | return medium; |
|
494 | return medium; | |
| 480 | } |
|
495 | } | |
| 481 |
|
496 | |||
| 482 |
public IPromise<TNew> |
|
497 | public IPromise<TNew> Chain<TNew>(ResultMapper<T, IPromise<TNew>> chained, ErrorHandler<IPromise<TNew>> error) { | |
| 483 |
return |
|
498 | return Chain(chained, error, null); | |
|
|
499 | } | |||
|
|
500 | ||||
|
|
501 | public IPromise<TNew> Chain<TNew>(ResultMapper<T, IPromise<TNew>> chained) { | |||
|
|
502 | return Chain(chained, null, null); | |||
| 484 | } |
|
503 | } | |
| 485 |
|
504 | |||
| 486 | public IPromise<T> Cancelled(Action handler) { |
|
505 | public IPromise<T> Cancelled(Action handler) { | |
| 487 |
var medium = new Promise<T>(this, |
|
506 | var medium = new Promise<T>(this,true); | |
| 488 | AddHandler(null, null, handler, medium); |
|
507 | AddHandler(null, null, handler, medium); | |
| 489 | return medium; |
|
508 | return medium; | |
| 490 | } |
|
509 | } | |
| @@ -494,9 +513,9 namespace Implab { | |||||
| 494 | /// </summary> |
|
513 | /// </summary> | |
| 495 | /// <param name="handler">The handler that will be called anyway</param> |
|
514 | /// <param name="handler">The handler that will be called anyway</param> | |
| 496 | /// <returns>self</returns> |
|
515 | /// <returns>self</returns> | |
| 497 |
public IPromise<T> |
|
516 | public IPromise<T> Anyway(Action handler) { | |
| 498 | if (handler == null) |
|
517 | Safe.ArgumentNotNull(handler, "handler"); | |
| 499 | throw new ArgumentNullException("handler"); |
|
518 | ||
| 500 | AddHandler( |
|
519 | AddHandler( | |
| 501 | x => handler(), |
|
520 | x => handler(), | |
| 502 | e => { |
|
521 | e => { | |
| @@ -541,7 +560,7 namespace Implab { | |||||
| 541 | /// <returns>Результат выполнения обещания</returns> |
|
560 | /// <returns>Результат выполнения обещания</returns> | |
| 542 | public T Join(int timeout) { |
|
561 | public T Join(int timeout) { | |
| 543 | var evt = new ManualResetEvent(false); |
|
562 | var evt = new ManualResetEvent(false); | |
| 544 |
|
|
563 | Anyway(() => evt.Set()); | |
| 545 |
|
564 | |||
| 546 | if (!evt.WaitOne(timeout, true)) |
|
565 | if (!evt.WaitOne(timeout, true)) | |
| 547 | throw new TimeoutException(); |
|
566 | throw new TimeoutException(); | |
| @@ -736,12 +755,49 namespace Implab { | |||||
| 736 |
|
755 | |||
| 737 | #region IPromiseBase explicit implementation |
|
756 | #region IPromiseBase explicit implementation | |
| 738 |
|
757 | |||
|
|
758 | IPromise IPromise.Then(Action success, ErrorHandler error, Action cancel) { | |||
|
|
759 | return Then( | |||
|
|
760 | x => success(), | |||
|
|
761 | e => { | |||
|
|
762 | error(e); | |||
|
|
763 | return default(T); | |||
|
|
764 | }, | |||
|
|
765 | cancel | |||
|
|
766 | ); | |||
|
|
767 | } | |||
|
|
768 | ||||
|
|
769 | IPromise IPromise.Then(Action success, ErrorHandler error) { | |||
|
|
770 | return Then( | |||
|
|
771 | x => success(), | |||
|
|
772 | e => { | |||
|
|
773 | error(e); | |||
|
|
774 | return default(T); | |||
|
|
775 | } | |||
|
|
776 | ); | |||
|
|
777 | } | |||
|
|
778 | ||||
|
|
779 | IPromise IPromise.Then(Action success) { | |||
|
|
780 | return Then(x => success()); | |||
|
|
781 | } | |||
|
|
782 | ||||
|
|
783 | void IPromise.Last(Action success, ErrorHandler error, Action cancel) { | |||
|
|
784 | Last(x => success(), error, cancel); | |||
|
|
785 | } | |||
|
|
786 | ||||
|
|
787 | void IPromise.Last(Action success, ErrorHandler error) { | |||
|
|
788 | Last(x => success(), error, null); | |||
|
|
789 | } | |||
|
|
790 | ||||
|
|
791 | void IPromise.Last(Action success) { | |||
|
|
792 | Last(x => success(), null, null); | |||
|
|
793 | } | |||
|
|
794 | ||||
| 739 | IPromise IPromise.Error(ErrorHandler error) { |
|
795 | IPromise IPromise.Error(ErrorHandler error) { | |
| 740 | return Error(error); |
|
796 | return Error(error); | |
| 741 | } |
|
797 | } | |
| 742 |
|
798 | |||
| 743 |
IPromise IPromise. |
|
799 | IPromise IPromise.Anyway(Action handler) { | |
| 744 |
return |
|
800 | return Anyway(handler); | |
| 745 | } |
|
801 | } | |
| 746 |
|
802 | |||
| 747 | IPromise IPromise.Cancelled(Action handler) { |
|
803 | IPromise IPromise.Cancelled(Action handler) { | |
| @@ -14,12 +14,10 namespace Implab { | |||||
| 14 |
|
14 | |||
| 15 | var p = new SyncContextPromise<T>(context, that, true); |
|
15 | var p = new SyncContextPromise<T>(context, that, true); | |
| 16 |
|
16 | |||
| 17 |
that. |
|
17 | that.Last( | |
| 18 |
|
|
18 | p.Resolve, | |
| 19 |
|
|
19 | p.Reject, | |
| 20 |
|
|
20 | p.Cancel | |
| 21 | return default(T); |
|
|||
| 22 | } |
|
|||
| 23 | ); |
|
21 | ); | |
| 24 | return p; |
|
22 | return p; | |
| 25 | } |
|
23 | } | |
| @@ -30,12 +28,10 namespace Implab { | |||||
| 30 |
|
28 | |||
| 31 | var p = new SyncContextPromise<T>(context, that, true); |
|
29 | var p = new SyncContextPromise<T>(context, that, true); | |
| 32 |
|
30 | |||
| 33 |
that. |
|
31 | that.Last( | |
| 34 |
|
|
32 | p.Resolve, | |
| 35 |
|
|
33 | p.Reject, | |
| 36 |
|
|
34 | p.Cancel | |
| 37 | return default(T); |
|
|||
| 38 | } |
|
|||
| 39 | ); |
|
35 | ); | |
| 40 | return p; |
|
36 | return p; | |
| 41 | } |
|
37 | } | |
| @@ -16,7 +16,7 using System.Runtime.InteropServices; | |||||
| 16 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, |
|
16 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, | |
| 17 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. |
|
17 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. | |
| 18 |
|
18 | |||
| 19 |
[assembly: AssemblyVersion(" |
|
19 | [assembly: AssemblyVersion("2.0.*")] | |
| 20 | [assembly: ComVisible(false)] |
|
20 | [assembly: ComVisible(false)] | |
| 21 |
|
21 | |||
| 22 | // The following attributes are used to specify the signing key for the assembly, |
|
22 | // The following attributes are used to specify the signing key for the assembly, | |
| @@ -92,14 +92,10 namespace Implab | |||||
| 92 | } |
|
92 | } | |
| 93 | } |
|
93 | } | |
| 94 |
|
94 | |||
| 95 |
public |
|
95 | public void Cancel() { | |
| 96 | lock (m_lock) { |
|
96 | lock (m_lock) { | |
| 97 |
if (!m_cancelled) |
|
97 | if (!m_cancelled) | |
| 98 | m_cancelled = true; |
|
98 | m_cancelled = true; | |
| 99 | return true; |
|
|||
| 100 | } else { |
|
|||
| 101 | return false; |
|
|||
| 102 | } |
|
|||
| 103 | } |
|
99 | } | |
| 104 | } |
|
100 | } | |
| 105 |
|
101 | |||
General Comments 0
You need to be logged in to leave comments.
Login now
