@@ -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 |
|
|
40 | } | |
86 | } |
|
41 | } | |
87 | } |
|
@@ -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> |
@@ -10,28 +10,34 namespace Implab { | |||||
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); |
|
|||
21 | void Last(ResultHandler<T> success); |
|
|||
22 |
|
26 | |||
23 |
IPromise<T> |
|
27 | IPromise<T2> Then<T2>(ResultMapper<T, T2> mapper, ErrorHandler<T2> error); | |
24 |
|
||||
25 | IPromise<T2> Then<T2>(ResultMapper<T,T2> mapper, ErrorHandler<T> error); |
|
|||
26 |
|
28 | |||
27 | IPromise<T2> Then<T2>(ResultMapper<T,T2> mapper); |
|
29 | IPromise<T2> Then<T2>(ResultMapper<T, T2> mapper); | |
28 |
|
30 | |||
29 |
IPromise<T2> |
|
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); | |||
30 |
|
34 | |||
31 |
IPromise<T2> |
|
35 | IPromise<T2> Chain<T2>(ResultMapper<T, IPromise<T2>> chained); | |
|
36 | ||||
|
37 | IPromise<T> Error(ErrorHandler<T> error); | |||
32 |
|
38 | |||
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( | |
|
124 | null, | |||
|
125 | null, | |||
|
126 | () => { | |||
125 | if (parent.IsExclusive) |
|
127 | if (parent.IsExclusive) | |
126 | parent.Cancel(); |
|
128 | parent.Cancel(); | |
127 |
} |
|
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 | } |
|
|||
220 | return false; |
|
|||
221 | } |
|
222 | } | |
222 |
|
||||
223 | // сделано для возвращаемого типа void |
|
|||
224 | protected void InternalCancel() { |
|
|||
225 | Cancel(); |
|
|||
226 | } |
|
223 | } | |
227 |
|
224 | |||
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) { |
|
|||
259 | return Then( |
|
|||
260 | x => success(), |
|
|||
261 | e => { |
|
|||
262 | error(e); |
|
|||
263 | return default(T); |
|
|||
264 | }, |
|
|||
265 | cancel |
|
|||
266 | ); |
|
|||
267 | } |
|
|||
268 |
|
254 | |||
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"); |
|
|||
377 |
|
354 | |||
378 | // создаем прицепленное обещание |
|
355 | // создаем прицепленное обещание | |
379 |
var |
|
356 | var medium = new Promise<TNew>(this, true); | |
380 |
|
357 | |||
381 |
ResultHandler<T> resultHandler = result => |
|
358 | ResultHandler<T> resultHandler = result => medium.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,32 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); |
|
434 | ); | |
443 |
|
|
435 | ||
|
436 | // notify chained operation that it's not needed anymore | |||
|
437 | // порядок вызова Then, Cancelled важен, поскольку от этого | |||
|
438 | // зависит IsExclusive | |||
|
439 | medium.Cancelled(() => { | |||
|
440 | if (promise.IsExclusive) | |||
|
441 | promise.Cancel(); | |||
|
442 | }); | |||
|
443 | }; | |||
|
444 | ||||
|
445 | ErrorHandler<T> errorHandler; | |||
|
446 | ||||
|
447 | if (error != null) | |||
|
448 | errorHandler = delegate(Exception e) { | |||
|
449 | try { | |||
|
450 | var promise = error(e); | |||
|
451 | ||||
|
452 | promise.Last( | |||
|
453 | medium.Resolve, | |||
|
454 | medium.Reject, | |||
|
455 | () => medium.Reject(new OperationCanceledException()) // внешняя отмена связанной операции рассматривается как ошибка | |||
444 | ); |
|
456 | ); | |
445 |
|
457 | |||
446 | // notify chained operation that it's not needed anymore |
|
458 | // notify chained operation that it's not needed anymore | |
@@ -450,37 +462,44 namespace Implab { | |||||
450 | if (promise.IsExclusive) |
|
462 | if (promise.IsExclusive) | |
451 | promise.Cancel(); |
|
463 | promise.Cancel(); | |
452 | }); |
|
464 | }); | |
453 |
|
465 | } catch (Exception e2) { | ||
454 | // внешняя отмена связанной операции рассматривается как ошибка |
|
466 | medium.Reject(e2); | |
455 | promise.Cancelled(() => medium.Reject(new OperationCanceledException())); |
|
467 | } | |
|
468 | return default(T); | |||
|
469 | }; | |||
|
470 | else | |||
|
471 | errorHandler = err => { | |||
|
472 | medium.Reject(err); | |||
|
473 | return default(T); | |||
456 | }; |
|
474 | }; | |
457 |
|
475 | |||
458 | ErrorHandler<T> errorHandler = delegate(Exception e) { |
|
476 | ||
459 | if (error != null) { |
|
477 | Action cancelHandler; | |
460 | try { |
|
478 | if (cancel != null) | |
461 | return error(e); |
|
479 | cancelHandler = () => { | |
462 |
|
|
480 | if (cancel != null) | |
463 |
|
|
481 | cancel(); | |
464 |
|
|
482 | medium.Cancel(); | |
465 | } |
|
|||
466 | } |
|
|||
467 | // в случае ошибки нужно передать исключение дальше по цепочке |
|
|||
468 | medium.Reject(e); |
|
|||
469 | return default(T); |
|
|||
470 | }; |
|
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) { | |
@@ -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