@@ -17,71 +17,25 namespace Implab.Fx | |||
|
17 | 17 | /// <example> |
|
18 | 18 | /// client |
|
19 | 19 | /// .Get("description.txt") // returns a promise |
|
20 |
/// .Di |
|
|
20 | /// .DispatchToControl(m_ctl) // handle the promise in the thread of the control | |
|
21 | 21 | /// .Then( |
|
22 | 22 | /// description => m_ctl.Text = description // now it's safe |
|
23 | 23 | /// ) |
|
24 | 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) | |
|
28 |
|
|
|
29 | if (ctl == null) | |
|
30 | throw new ArgumentNullException("ctl"); | |
|
27 | Safe.ArgumentNotNull(that, "that"); | |
|
28 | Safe.ArgumentNotNull(ctl, "ctl"); | |
|
31 | 29 | |
|
32 | 30 | var directed = new ControlBoundPromise<T>(ctl,that,true); |
|
33 | 31 | |
|
34 |
that. |
|
|
32 | that.Last( | |
|
35 | 33 | directed.Resolve, |
|
36 |
|
|
|
37 |
|
|
|
38 | directed.Reject(err); | |
|
39 | return default(T); | |
|
40 | } | |
|
34 | directed.Reject, | |
|
35 | directed.Cancel | |
|
41 | 36 | ); |
|
42 | 37 | |
|
43 | 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 | 32 | // You can specify all the values or you can default the Build and Revision Numbers |
|
33 | 33 | // by using the '*' as shown below: |
|
34 | 34 | // [assembly: AssemblyVersion("1.0.*")] |
|
35 |
[assembly: AssemblyVersion(" |
|
|
36 | [assembly: AssemblyFileVersion("1.0.0.0")] | |
|
35 | [assembly: AssemblyVersion("2.0.*")] |
@@ -38,6 +38,38 namespace Implab.Test { | |||
|
38 | 38 | } |
|
39 | 39 | |
|
40 | 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 | 73 | public void JoinSuccessTest() { |
|
42 | 74 | var p = new Promise<int>(); |
|
43 | 75 | p.Resolve(100); |
@@ -63,7 +95,7 namespace Implab.Test { | |||
|
63 | 95 | public void MapTest() { |
|
64 | 96 | var p = new Promise<int>(); |
|
65 | 97 | |
|
66 |
var p2 = p. |
|
|
98 | var p2 = p.Then(x => x.ToString()); | |
|
67 | 99 | p.Resolve(100); |
|
68 | 100 | |
|
69 | 101 | Assert.AreEqual(p2.Join(), "100"); |
@@ -185,8 +217,8 namespace Implab.Test { | |||
|
185 | 217 | var stop = new ManualResetEvent(false); |
|
186 | 218 | int total = 0; |
|
187 | 219 | |
|
188 | int itemsPerWriter = 1000; | |
|
189 |
int writersCount = |
|
|
220 | int itemsPerWriter = 10000; | |
|
221 | int writersCount = 10; | |
|
190 | 222 | |
|
191 | 223 | for (int i = 0; i < writersCount; i++) { |
|
192 | 224 | Interlocked.Increment(ref writers); |
@@ -318,7 +350,7 namespace Implab.Test { | |||
|
318 | 350 | .Chain(x => |
|
319 | 351 | PromiseHelper |
|
320 | 352 | .Sleep(200, "Hi, " + x) |
|
321 |
. |
|
|
353 | .Then(y => y) | |
|
322 | 354 | .Cancelled(() => flags[1] = true) |
|
323 | 355 | ) |
|
324 | 356 | .Cancelled(() => flags[2] = true); |
@@ -341,7 +373,7 namespace Implab.Test { | |||
|
341 | 373 | // завершаться ошибкой OperationCanceledException |
|
342 | 374 | var p = PromiseHelper |
|
343 | 375 | .Sleep(1, "Hi, HAL!") |
|
344 |
. |
|
|
376 | .Then(x => { | |
|
345 | 377 | // запускаем две асинхронные операции |
|
346 | 378 | var result = PromiseHelper.Sleep(1000, "HEM ENABLED!!!"); |
|
347 | 379 | // вторая операция отменяет первую до завершения |
@@ -360,16 +392,15 namespace Implab.Test { | |||
|
360 | 392 | [TestMethod] |
|
361 | 393 | public void ChainedCancel2Test() { |
|
362 | 394 | // при отмене цепочки обещаний, вложенные операции также должны отменяться |
|
363 | IPromise p = null; | |
|
364 | 395 | var pSurvive = new Promise<bool>(); |
|
365 | 396 | var hemStarted = new ManualResetEvent(false); |
|
366 | p = PromiseHelper | |
|
397 | var p = PromiseHelper | |
|
367 | 398 | .Sleep(1, "Hi, HAL!") |
|
368 | 399 | .Chain(x => { |
|
369 | 400 | hemStarted.Set(); |
|
370 | 401 | // запускаем две асинхронные операции |
|
371 | 402 | var result = PromiseHelper |
|
372 | .Sleep(1000, "HEM ENABLED!!!") | |
|
403 | .Sleep(10000, "HEM ENABLED!!!") | |
|
373 | 404 | .Then(s => pSurvive.Resolve(false)); |
|
374 | 405 | |
|
375 | 406 | result |
@@ -215,7 +215,7 namespace Implab.Diagnostics { | |||
|
215 | 215 | Safe.ArgumentNotNull(promise, "promise"); |
|
216 | 216 | |
|
217 | 217 | var ctx = DetachLogicalOperation(); |
|
218 |
promise. |
|
|
218 | promise.Anyway(() => { | |
|
219 | 219 | var old = _current; |
|
220 | 220 | TraceContext.Attach(ctx); |
|
221 | 221 | TraceContext.Current.EndLogicalOperation(); |
@@ -5,6 +5,6 using System.Text; | |||
|
5 | 5 | |
|
6 | 6 | namespace Implab { |
|
7 | 7 | public interface ICancellable { |
|
8 |
|
|
|
8 | void Cancel(); | |
|
9 | 9 | } |
|
10 | 10 | } |
@@ -52,7 +52,7 namespace Implab { | |||
|
52 | 52 | /// </summary> |
|
53 | 53 | /// <param name="handler">Обработчик.</param> |
|
54 | 54 | /// <remarks>После обработке ошибки, она передается дальше.</remarks> |
|
55 |
IPromise |
|
|
55 | IPromise Anyway(Action handler); | |
|
56 | 56 | /// <summary> |
|
57 | 57 | /// Обработчик для регистрации отмены обещания, событие отмены не может быть подавлено. |
|
58 | 58 | /// </summary> |
@@ -10,28 +10,34 namespace Implab { | |||
|
10 | 10 | |
|
11 | 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 | 19 | IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error, Action cancel); |
|
14 | 20 | |
|
15 | 21 | IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error); |
|
16 | 22 | |
|
17 | 23 | IPromise<T> Then(ResultHandler<T> success); |
|
18 | 24 | |
|
19 |
|
|
|
20 | void Last(ResultHandler<T> success, ErrorHandler error); | |
|
21 | void Last(ResultHandler<T> success); | |
|
25 | IPromise<T2> Then<T2>(ResultMapper<T, T2> mapper, ErrorHandler<T2> error, Action cancel); | |
|
22 | 26 | |
|
23 |
IPromise<T> |
|
|
24 | ||
|
25 | IPromise<T2> Then<T2>(ResultMapper<T,T2> mapper, ErrorHandler<T> error); | |
|
27 | IPromise<T2> Then<T2>(ResultMapper<T, T2> mapper, ErrorHandler<T2> error); | |
|
26 | 28 | |
|
27 | 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 | 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 | 29 | m_pending = source.Length; |
|
30 | 30 | m_action = action; |
|
31 | 31 | |
|
32 |
m_promise. |
|
|
32 | m_promise.Anyway(Dispose); | |
|
33 | 33 | |
|
34 | 34 | InitPool(); |
|
35 | 35 | } |
@@ -85,7 +85,7 namespace Implab.Parallels { | |||
|
85 | 85 | m_transform = transform; |
|
86 | 86 | m_traceContext = TraceContext.Snapshot(); |
|
87 | 87 | |
|
88 |
m_promise. |
|
|
88 | m_promise.Anyway(Dispose); | |
|
89 | 89 | |
|
90 | 90 | InitPool(); |
|
91 | 91 | } |
@@ -138,7 +138,7 namespace Implab.Parallels { | |||
|
138 | 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 | 142 | if (source == null) |
|
143 | 143 | throw new ArgumentNullException("source"); |
|
144 | 144 | if (transform == null) |
@@ -165,7 +165,7 namespace Implab.Parallels { | |||
|
165 | 165 | semaphore.WaitOne(); |
|
166 | 166 | try { |
|
167 | 167 | var p1 = transform(source[i]); |
|
168 |
p1. |
|
|
168 | p1.Anyway(() => semaphore.Release()); | |
|
169 | 169 | p1.Then( |
|
170 | 170 | x => { |
|
171 | 171 | res[idx] = x; |
@@ -186,7 +186,7 namespace Implab.Parallels { | |||
|
186 | 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 | 11 | public delegate T ErrorHandler<out T>(Exception e); |
|
12 | 12 | public delegate void ResultHandler<in T>(T result); |
|
13 | 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 | 15 | /// <summary> |
|
17 | 16 | /// Класс для асинхронного получения результатов. Так называемое "обещание". |
@@ -121,10 +120,15 namespace Implab { | |||
|
121 | 120 | public Promise(IPromise parent, bool cancellable) { |
|
122 | 121 | m_cancellable = cancellable; |
|
123 | 122 | if (parent != null) |
|
124 |
|
|
|
123 | AddHandler( | |
|
124 | null, | |
|
125 | null, | |
|
126 | () => { | |
|
125 | 127 | if (parent.IsExclusive) |
|
126 | 128 | parent.Cancel(); |
|
127 |
} |
|
|
129 | }, | |
|
130 | null | |
|
131 | ); | |
|
128 | 132 | } |
|
129 | 133 | |
|
130 | 134 | bool BeginTransit() { |
@@ -210,22 +214,14 namespace Implab { | |||
|
210 | 214 | /// <summary> |
|
211 | 215 | /// Отменяет операцию, если это возможно. |
|
212 | 216 | /// </summary> |
|
213 | /// <returns><c>true</c> Операция была отменена, обработчики не будут вызваны.<c>false</c> отмена не возможна, поскольку обещание уже выполнено и обработчики отработали.</returns> | |
|
214 |
public |
|
|
217 | /// <remarks>Для определения была ли операция отменена следует использовать свойство <see cref="IsCancelled"/>.</remarks> | |
|
218 | public void Cancel() { | |
|
215 | 219 | if (m_cancellable && BeginTransit()) { |
|
216 | 220 | CompleteTransit(CANCELLED_STATE); |
|
217 | 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 | 225 | public IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error, Action cancel) { |
|
230 | 226 | if (success == null && error == null && cancel == null) |
|
231 | 227 | return this; |
@@ -255,30 +251,7 namespace Implab { | |||
|
255 | 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 | 257 | public IPromise<T> Then(ResultHandler<T> success) { |
@@ -292,6 +265,23 namespace Implab { | |||
|
292 | 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 | 285 | public void Last(ResultHandler<T> success, ErrorHandler error, Action cancel) { |
|
296 | 286 | if (success == null && error == null && cancel == null) |
|
297 | 287 | return; |
@@ -313,18 +303,6 namespace Implab { | |||
|
313 | 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 | 306 | public IPromise Error(ErrorHandler error) { |
|
329 | 307 | if (error == null) |
|
330 | 308 | return this; |
@@ -371,44 +349,56 namespace Implab { | |||
|
371 | 349 | /// <param name="error">Обработчик ошибки. Данный обработчик получит |
|
372 | 350 | /// исключение возникшее при выполнении операции.</param> |
|
373 | 351 | /// <returns>Новое обещание, которое будет выполнено при выполнении исходного обещания.</returns> |
|
374 | public IPromise<TNew> Then<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler<T> error) { | |
|
375 | if (mapper == null) | |
|
376 | throw new ArgumentNullException("mapper"); | |
|
352 | public IPromise<TNew> Then<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler<TNew> error, Action cancel) { | |
|
353 | Safe.ArgumentNotNull(mapper, "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 | 359 | ErrorHandler<T> errorHandler; |
|
383 | 360 | if (error != null) |
|
384 | 361 | errorHandler = e => { |
|
385 | 362 | try { |
|
386 |
|
|
|
363 | medium.Resolve(error(e)); | |
|
387 | 364 | } catch (Exception e2) { |
|
388 | 365 | // в случае ошибки нужно передать исключение дальше по цепочке |
|
389 |
|
|
|
366 | medium.Reject(e2); | |
|
390 | 367 | } |
|
391 | 368 | return default(T); |
|
392 | 369 | }; |
|
393 | 370 | else |
|
394 | 371 | errorHandler = e => { |
|
395 |
|
|
|
372 | medium.Reject(e); | |
|
396 | 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 | 386 | AddHandler( |
|
401 | 387 | resultHandler, |
|
402 | 388 | errorHandler, |
|
403 |
c |
|
|
389 | cancelHandler, | |
|
404 | 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 | 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 | 404 | /// <summary> |
@@ -421,7 +411,9 namespace Implab { | |||
|
421 | 411 | /// <param name="error">Обработчик ошибки. Данный обработчик получит |
|
422 | 412 | /// исключение возникшее при выполнении текуещй операции.</param> |
|
423 | 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 | 428 | var promise = chained(result); |
|
437 | 429 | |
|
438 |
promise. |
|
|
430 | promise.Last( | |
|
439 | 431 | medium.Resolve, |
|
440 |
|
|
|
441 | medium.Reject(err); | |
|
442 | throw new TransientPromiseException(err); | |
|
443 |
|
|
|
432 | medium.Reject, | |
|
433 | () => medium.Reject(new OperationCanceledException()) // внешняя отмена связанной операции рассматривается как ошибка | |
|
434 | ); | |
|
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 | 458 | // notify chained operation that it's not needed anymore |
@@ -450,37 +462,44 namespace Implab { | |||
|
450 | 462 | if (promise.IsExclusive) |
|
451 | 463 | promise.Cancel(); |
|
452 | 464 | }); |
|
453 | ||
|
454 | // внешняя отмена связанной операции рассматривается как ошибка | |
|
455 | promise.Cancelled(() => medium.Reject(new OperationCanceledException())); | |
|
465 | } catch (Exception e2) { | |
|
466 | medium.Reject(e2); | |
|
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) { | |
|
459 | if (error != null) { | |
|
460 | try { | |
|
461 | return error(e); | |
|
462 |
|
|
|
463 |
|
|
|
464 |
|
|
|
465 | } | |
|
466 | } | |
|
467 | // в случае ошибки нужно передать исключение дальше по цепочке | |
|
468 | medium.Reject(e); | |
|
469 | return default(T); | |
|
476 | ||
|
477 | Action cancelHandler; | |
|
478 | if (cancel != null) | |
|
479 | cancelHandler = () => { | |
|
480 | if (cancel != null) | |
|
481 | cancel(); | |
|
482 | medium.Cancel(); | |
|
470 | 483 | }; |
|
484 | else | |
|
485 | cancelHandler = medium.Cancel; | |
|
471 | 486 | |
|
472 | 487 | AddHandler( |
|
473 | 488 | resultHandler, |
|
474 | 489 | errorHandler, |
|
475 |
|
|
|
490 | cancelHandler, | |
|
476 | 491 | null |
|
477 | 492 | ); |
|
478 | 493 | |
|
479 | 494 | return medium; |
|
480 | 495 | } |
|
481 | 496 | |
|
482 |
public IPromise<TNew> |
|
|
483 |
return |
|
|
497 | public IPromise<TNew> Chain<TNew>(ResultMapper<T, IPromise<TNew>> chained, ErrorHandler<IPromise<TNew>> error) { | |
|
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 | 505 | public IPromise<T> Cancelled(Action handler) { |
@@ -494,9 +513,9 namespace Implab { | |||
|
494 | 513 | /// </summary> |
|
495 | 514 | /// <param name="handler">The handler that will be called anyway</param> |
|
496 | 515 | /// <returns>self</returns> |
|
497 |
public IPromise<T> |
|
|
498 | if (handler == null) | |
|
499 | throw new ArgumentNullException("handler"); | |
|
516 | public IPromise<T> Anyway(Action handler) { | |
|
517 | Safe.ArgumentNotNull(handler, "handler"); | |
|
518 | ||
|
500 | 519 | AddHandler( |
|
501 | 520 | x => handler(), |
|
502 | 521 | e => { |
@@ -541,7 +560,7 namespace Implab { | |||
|
541 | 560 | /// <returns>Результат выполнения обещания</returns> |
|
542 | 561 | public T Join(int timeout) { |
|
543 | 562 | var evt = new ManualResetEvent(false); |
|
544 |
|
|
|
563 | Anyway(() => evt.Set()); | |
|
545 | 564 | |
|
546 | 565 | if (!evt.WaitOne(timeout, true)) |
|
547 | 566 | throw new TimeoutException(); |
@@ -736,12 +755,49 namespace Implab { | |||
|
736 | 755 | |
|
737 | 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 | 795 | IPromise IPromise.Error(ErrorHandler error) { |
|
740 | 796 | return Error(error); |
|
741 | 797 | } |
|
742 | 798 | |
|
743 |
IPromise IPromise. |
|
|
744 |
return |
|
|
799 | IPromise IPromise.Anyway(Action handler) { | |
|
800 | return Anyway(handler); | |
|
745 | 801 | } |
|
746 | 802 | |
|
747 | 803 | IPromise IPromise.Cancelled(Action handler) { |
@@ -14,12 +14,10 namespace Implab { | |||
|
14 | 14 | |
|
15 | 15 | var p = new SyncContextPromise<T>(context, that, true); |
|
16 | 16 | |
|
17 |
that. |
|
|
18 |
|
|
|
19 |
|
|
|
20 |
|
|
|
21 | return default(T); | |
|
22 | } | |
|
17 | that.Last( | |
|
18 | p.Resolve, | |
|
19 | p.Reject, | |
|
20 | p.Cancel | |
|
23 | 21 | ); |
|
24 | 22 | return p; |
|
25 | 23 | } |
@@ -30,12 +28,10 namespace Implab { | |||
|
30 | 28 | |
|
31 | 29 | var p = new SyncContextPromise<T>(context, that, true); |
|
32 | 30 | |
|
33 |
that. |
|
|
34 |
|
|
|
35 |
|
|
|
36 |
|
|
|
37 | return default(T); | |
|
38 | } | |
|
31 | that.Last( | |
|
32 | p.Resolve, | |
|
33 | p.Reject, | |
|
34 | p.Cancel | |
|
39 | 35 | ); |
|
40 | 36 | return p; |
|
41 | 37 | } |
@@ -16,7 +16,7 using System.Runtime.InteropServices; | |||
|
16 | 16 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, |
|
17 | 17 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. |
|
18 | 18 | |
|
19 |
[assembly: AssemblyVersion(" |
|
|
19 | [assembly: AssemblyVersion("2.0.*")] | |
|
20 | 20 | [assembly: ComVisible(false)] |
|
21 | 21 | |
|
22 | 22 | // The following attributes are used to specify the signing key for the assembly, |
General Comments 0
You need to be logged in to leave comments.
Login now