##// END OF EJS Templates
Added the chaining method to the non-generic IPromise
cin -
r96:daffa72a1cec v2
parent child
Show More
@@ -1,82 +1,86
1 using System;
1 using System;
2 using System.Collections.Generic;
2 using System.Collections.Generic;
3 using System.Linq;
3 using System.Linq;
4 using System.Text;
4 using System.Text;
5
5
6 namespace Implab {
6 namespace Implab {
7 public interface IPromise: ICancellable {
7 public interface IPromise: ICancellable {
8 /// <summary>
8 /// <summary>
9 /// Check whereather the promise has no more than one dependent promise.
9 /// Check whereather the promise has no more than one dependent promise.
10 /// </summary>
10 /// </summary>
11 bool IsExclusive {
11 bool IsExclusive {
12 get;
12 get;
13 }
13 }
14
14
15 /// <summary>
15 /// <summary>
16 /// Тип результата, получаемого через данное обещание.
16 /// Тип результата, получаемого через данное обещание.
17 /// </summary>
17 /// </summary>
18 Type PromiseType { get; }
18 Type PromiseType { get; }
19
19
20 /// <summary>
20 /// <summary>
21 /// Обещание является выполненым, либо успешно, либо с ошибкой.
21 /// Обещание является выполненым, либо успешно, либо с ошибкой.
22 /// </summary>
22 /// </summary>
23 bool IsResolved { get; }
23 bool IsResolved { get; }
24
24
25 /// <summary>
25 /// <summary>
26 /// Обещание было отменено.
26 /// Обещание было отменено.
27 /// </summary>
27 /// </summary>
28 bool IsCancelled { get; }
28 bool IsCancelled { get; }
29
29
30 IPromise Then(Action success, ErrorHandler error, Action cancel);
30 IPromise Then(Action success, ErrorHandler error, Action cancel);
31 IPromise Then(Action success, ErrorHandler error);
31 IPromise Then(Action success, ErrorHandler error);
32 IPromise Then(Action success);
32 IPromise Then(Action success);
33
33
34 IPromise Chain(Func<IPromise> chained, ErrorHandler<IPromise> error, Action cancel);
35 IPromise Chain(Func<IPromise> chained, ErrorHandler<IPromise> error);
36 IPromise Chain(Func<IPromise> chained);
37
34 /// <summary>
38 /// <summary>
35 /// Добавляет последнй обработчик в цепочку обещаний, не создает промежуточных обещаний.
39 /// Добавляет последнй обработчик в цепочку обещаний, не создает промежуточных обещаний.
36 /// </summary>
40 /// </summary>
37 /// <param name="success">Success.</param>
41 /// <param name="success">Success.</param>
38 /// <param name="error">Error.</param>
42 /// <param name="error">Error.</param>
39 /// <param name="cancel">Cancel.</param>
43 /// <param name="cancel">Cancel.</param>
40 void Last(Action success, ErrorHandler error, Action cancel);
44 void Last(Action success, ErrorHandler error, Action cancel);
41 void Last(Action success, ErrorHandler error);
45 void Last(Action success, ErrorHandler error);
42 void Last(Action success);
46 void Last(Action success);
43
47
44 IPromise Error(ErrorHandler error);
48 IPromise Error(ErrorHandler error);
45 /// <summary>
49 /// <summary>
46 /// Обрабатывает либо ошибку, либо результат. Событие отмены не обрабатывается.
50 /// Обрабатывает либо ошибку, либо результат. Событие отмены не обрабатывается.
47 /// </summary>
51 /// </summary>
48 /// <param name="handler">Обработчик.</param>
52 /// <param name="handler">Обработчик.</param>
49 /// <remarks>После обработке ошибки, она передается дальше.</remarks>
53 /// <remarks>После обработке ошибки, она передается дальше.</remarks>
50 /// <summary>
54 /// <summary>
51 /// Обрабатывает либо ошибку, либо результат, либо отмену обещания.
55 /// Обрабатывает либо ошибку, либо результат, либо отмену обещания.
52 /// </summary>
56 /// </summary>
53 /// <param name="handler">Обработчик.</param>
57 /// <param name="handler">Обработчик.</param>
54 /// <remarks>После обработке ошибки, она передается дальше.</remarks>
58 /// <remarks>После обработке ошибки, она передается дальше.</remarks>
55 IPromise Anyway(Action handler);
59 IPromise Anyway(Action handler);
56 /// <summary>
60 /// <summary>
57 /// Обработчик для регистрации отмены обещания, событие отмены не может быть подавлено.
61 /// Обработчик для регистрации отмены обещания, событие отмены не может быть подавлено.
58 /// </summary>
62 /// </summary>
59 /// <returns>Новое обещание, связанное с текущим.</returns>
63 /// <returns>Новое обещание, связанное с текущим.</returns>
60 /// <param name="handler">Обработчик события.</param>
64 /// <param name="handler">Обработчик события.</param>
61 /// <remarks>Если обработчик вызывает исключение, то оно передается обработчику ошибки, результат работы
65 /// <remarks>Если обработчик вызывает исключение, то оно передается обработчику ошибки, результат работы
62 /// которого будет передан связанному обещанию</remarks>
66 /// которого будет передан связанному обещанию</remarks>
63 IPromise Cancelled(Action handler);
67 IPromise Cancelled(Action handler);
64
68
65 /// <summary>
69 /// <summary>
66 /// Преобразует результат обещания к заданному типу и возвращает новое обещание.
70 /// Преобразует результат обещания к заданному типу и возвращает новое обещание.
67 /// </summary>
71 /// </summary>
68 IPromise<T> Cast<T>();
72 IPromise<T> Cast<T>();
69
73
70 /// <summary>
74 /// <summary>
71 /// Синхронизирует текущий поток с обещанием.
75 /// Синхронизирует текущий поток с обещанием.
72 /// </summary>
76 /// </summary>
73 void Join();
77 void Join();
74 /// <summary>
78 /// <summary>
75 /// Синхронизирует текущий поток с обещанием.
79 /// Синхронизирует текущий поток с обещанием.
76 /// </summary>
80 /// </summary>
77 /// <param name="timeout">Время ожидания, по его истечению возникнет исключение.</param>
81 /// <param name="timeout">Время ожидания, по его истечению возникнет исключение.</param>
78 /// <exception cref="TimeoutException">Превышено время ожидания.</exception>
82 /// <exception cref="TimeoutException">Превышено время ожидания.</exception>
79 void Join(int timeout);
83 void Join(int timeout);
80
84
81 }
85 }
82 }
86 }
@@ -1,822 +1,914
1 using System;
1 using System;
2 using System.Collections.Generic;
2 using System.Collections.Generic;
3 using System.Reflection;
3 using System.Reflection;
4 using System.Diagnostics;
4 using System.Diagnostics;
5 using System.Threading;
5 using System.Threading;
6 using Implab.Parallels;
6 using Implab.Parallels;
7
7
8 namespace Implab {
8 namespace Implab {
9
9
10 public delegate void ErrorHandler(Exception e);
10 public delegate void ErrorHandler(Exception e);
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
14
15 /// <summary>
15 /// <summary>
16 /// Класс для асинхронного получения результатов. Так называемое "обещание".
16 /// Класс для асинхронного получения результатов. Так называемое "обещание".
17 /// </summary>
17 /// </summary>
18 /// <typeparam name="T">Тип получаемого результата</typeparam>
18 /// <typeparam name="T">Тип получаемого результата</typeparam>
19 /// <remarks>
19 /// <remarks>
20 /// <para>Сервис при обращении к его методу дает обещаиние о выполнении операции,
20 /// <para>Сервис при обращении к его методу дает обещаиние о выполнении операции,
21 /// клиент получив такое обещание может установить ряд обратных вызово для получения
21 /// клиент получив такое обещание может установить ряд обратных вызово для получения
22 /// событий выполнения обещания, тоесть завершения операции и предоставлении результатов.</para>
22 /// событий выполнения обещания, тоесть завершения операции и предоставлении результатов.</para>
23 /// <para>
23 /// <para>
24 /// Обещение может быть как выполнено, так и выполнено с ошибкой. Для подписки на
24 /// Обещение может быть как выполнено, так и выполнено с ошибкой. Для подписки на
25 /// данные события клиент должен использовать методы <c>Then</c>.
25 /// данные события клиент должен использовать методы <c>Then</c>.
26 /// </para>
26 /// </para>
27 /// <para>
27 /// <para>
28 /// Сервис, в свою очередь, по окончанию выполнения операции (возможно с ошибкой),
28 /// Сервис, в свою очередь, по окончанию выполнения операции (возможно с ошибкой),
29 /// использует методы <c>Resolve</c> либо <c>Reject</c> для оповещения клиетна о
29 /// использует методы <c>Resolve</c> либо <c>Reject</c> для оповещения клиетна о
30 /// выполнении обещания.
30 /// выполнении обещания.
31 /// </para>
31 /// </para>
32 /// <para>
32 /// <para>
33 /// Если сервер успел выполнить обещание еще до того, как клиент на него подписался,
33 /// Если сервер успел выполнить обещание еще до того, как клиент на него подписался,
34 /// то в момент подписки клиента будут вызваны соответсвующие события в синхронном
34 /// то в момент подписки клиента будут вызваны соответсвующие события в синхронном
35 /// режиме и клиент будет оповещен в любом случае. Иначе, обработчики добавляются в
35 /// режиме и клиент будет оповещен в любом случае. Иначе, обработчики добавляются в
36 /// список в порядке подписания и в этом же порядке они будут вызваны при выполнении
36 /// список в порядке подписания и в этом же порядке они будут вызваны при выполнении
37 /// обещания.
37 /// обещания.
38 /// </para>
38 /// </para>
39 /// <para>
39 /// <para>
40 /// Обрабатывая результаты обещания можно преобразовывать результаты либо инициировать
40 /// Обрабатывая результаты обещания можно преобразовывать результаты либо инициировать
41 /// связанные асинхронные операции, которые также возвращают обещания. Для этого следует
41 /// связанные асинхронные операции, которые также возвращают обещания. Для этого следует
42 /// использовать соответствующую форму методе <c>Then</c>.
42 /// использовать соответствующую форму методе <c>Then</c>.
43 /// </para>
43 /// </para>
44 /// <para>
44 /// <para>
45 /// Также хорошим правилом является то, что <c>Resolve</c> и <c>Reject</c> должен вызывать
45 /// Также хорошим правилом является то, что <c>Resolve</c> и <c>Reject</c> должен вызывать
46 /// только инициатор обещания иначе могут возникнуть противоречия.
46 /// только инициатор обещания иначе могут возникнуть противоречия.
47 /// </para>
47 /// </para>
48 /// </remarks>
48 /// </remarks>
49 public class Promise<T> : IPromise<T> {
49 public class Promise<T> : IPromise<T> {
50
50
51 protected struct HandlerDescriptor {
51 protected struct HandlerDescriptor {
52 public ResultHandler<T> resultHandler;
52 public ResultHandler<T> resultHandler;
53 public ErrorHandler<T> errorHandler;
53 public ErrorHandler<T> errorHandler;
54 public Action cancellHandler;
54 public Action cancellHandler;
55 public Promise<T> medium;
55 public Promise<T> medium;
56
56
57 public void Resolve(T result) {
57 public void Resolve(T result) {
58 if (resultHandler != null) {
58 if (resultHandler != null) {
59 try {
59 try {
60 resultHandler(result);
60 resultHandler(result);
61 } catch (Exception e) {
61 } catch (Exception e) {
62 Reject(e);
62 Reject(e);
63 return;
63 return;
64 }
64 }
65 }
65 }
66 if (medium != null)
66 if (medium != null)
67 medium.Resolve(result);
67 medium.Resolve(result);
68 }
68 }
69
69
70 public void Reject(Exception err) {
70 public void Reject(Exception err) {
71 if (errorHandler != null) {
71 if (errorHandler != null) {
72 try {
72 try {
73 var res = errorHandler(err);
73 var res = errorHandler(err);
74 if (medium != null)
74 if (medium != null)
75 medium.Resolve(res);
75 medium.Resolve(res);
76 } catch (TransientPromiseException err2) {
76 } catch (TransientPromiseException err2) {
77 if (medium != null)
77 if (medium != null)
78 medium.Reject(err2.InnerException);
78 medium.Reject(err2.InnerException);
79 } catch (Exception err2) {
79 } catch (Exception err2) {
80 if (medium != null)
80 if (medium != null)
81 medium.Reject(err2);
81 medium.Reject(err2);
82 }
82 }
83 } else if (medium != null)
83 } else if (medium != null)
84 medium.Reject(err);
84 medium.Reject(err);
85 }
85 }
86
86
87 public void Cancel() {
87 public void Cancel() {
88 if (cancellHandler != null) {
88 if (cancellHandler != null) {
89 try {
89 try {
90 cancellHandler();
90 cancellHandler();
91 } catch (Exception err) {
91 } catch (Exception err) {
92 Reject(err);
92 Reject(err);
93 return;
93 return;
94 }
94 }
95 }
95 }
96 if (medium != null)
96 if (medium != null)
97 medium.Cancel();
97 medium.Cancel();
98 }
98 }
99 }
99 }
100
100
101 const int UNRESOLVED_SATE = 0;
101 const int UNRESOLVED_SATE = 0;
102 const int TRANSITIONAL_STATE = 1;
102 const int TRANSITIONAL_STATE = 1;
103 const int SUCCEEDED_STATE = 2;
103 const int SUCCEEDED_STATE = 2;
104 const int REJECTED_STATE = 3;
104 const int REJECTED_STATE = 3;
105 const int CANCELLED_STATE = 4;
105 const int CANCELLED_STATE = 4;
106
106
107 readonly bool m_cancellable;
107 readonly bool m_cancellable;
108
108
109 int m_childrenCount = 0;
109 int m_childrenCount = 0;
110 int m_state;
110 int m_state;
111 T m_result;
111 T m_result;
112 Exception m_error;
112 Exception m_error;
113
113
114 readonly MTQueue<HandlerDescriptor> m_handlers = new MTQueue<HandlerDescriptor>();
114 readonly MTQueue<HandlerDescriptor> m_handlers = new MTQueue<HandlerDescriptor>();
115
115
116 public Promise() {
116 public Promise() {
117 m_cancellable = true;
117 m_cancellable = true;
118 }
118 }
119
119
120 public Promise(IPromise parent, bool cancellable) {
120 public Promise(IPromise parent, bool cancellable) {
121 m_cancellable = cancellable;
121 m_cancellable = cancellable;
122 if (parent != null)
122 if (parent != null)
123 AddHandler(
123 AddHandler(
124 null,
124 null,
125 null,
125 null,
126 () => {
126 () => {
127 if (parent.IsExclusive)
127 if (parent.IsExclusive)
128 parent.Cancel();
128 parent.Cancel();
129 },
129 },
130 null
130 null
131 );
131 );
132 }
132 }
133
133
134 bool BeginTransit() {
134 bool BeginTransit() {
135 return UNRESOLVED_SATE == Interlocked.CompareExchange(ref m_state, TRANSITIONAL_STATE, UNRESOLVED_SATE);
135 return UNRESOLVED_SATE == Interlocked.CompareExchange(ref m_state, TRANSITIONAL_STATE, UNRESOLVED_SATE);
136 }
136 }
137
137
138 void CompleteTransit(int state) {
138 void CompleteTransit(int state) {
139 if (TRANSITIONAL_STATE != Interlocked.CompareExchange(ref m_state, state, TRANSITIONAL_STATE))
139 if (TRANSITIONAL_STATE != Interlocked.CompareExchange(ref m_state, state, TRANSITIONAL_STATE))
140 throw new InvalidOperationException("Can't complete transition when the object isn't in the transitional state");
140 throw new InvalidOperationException("Can't complete transition when the object isn't in the transitional state");
141 }
141 }
142
142
143 void WaitTransition() {
143 void WaitTransition() {
144 while (m_state == TRANSITIONAL_STATE) {
144 while (m_state == TRANSITIONAL_STATE) {
145 Thread.MemoryBarrier();
145 Thread.MemoryBarrier();
146 }
146 }
147 }
147 }
148
148
149 public bool IsResolved {
149 public bool IsResolved {
150 get {
150 get {
151 Thread.MemoryBarrier();
151 Thread.MemoryBarrier();
152 return m_state > 1;
152 return m_state > 1;
153 }
153 }
154 }
154 }
155
155
156 public bool IsCancelled {
156 public bool IsCancelled {
157 get {
157 get {
158 Thread.MemoryBarrier();
158 Thread.MemoryBarrier();
159 return m_state == CANCELLED_STATE;
159 return m_state == CANCELLED_STATE;
160 }
160 }
161 }
161 }
162
162
163 public Type PromiseType {
163 public Type PromiseType {
164 get { return typeof(T); }
164 get { return typeof(T); }
165 }
165 }
166
166
167 /// <summary>
167 /// <summary>
168 /// Выполняет обещание, сообщая об успешном выполнении.
168 /// Выполняет обещание, сообщая об успешном выполнении.
169 /// </summary>
169 /// </summary>
170 /// <param name="result">Результат выполнения.</param>
170 /// <param name="result">Результат выполнения.</param>
171 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception>
171 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception>
172 public void Resolve(T result) {
172 public void Resolve(T result) {
173 if (BeginTransit()) {
173 if (BeginTransit()) {
174 m_result = result;
174 m_result = result;
175 CompleteTransit(SUCCEEDED_STATE);
175 CompleteTransit(SUCCEEDED_STATE);
176 OnStateChanged();
176 OnStateChanged();
177 } else {
177 } else {
178 WaitTransition();
178 WaitTransition();
179 if (m_state != CANCELLED_STATE)
179 if (m_state != CANCELLED_STATE)
180 throw new InvalidOperationException("The promise is already resolved");
180 throw new InvalidOperationException("The promise is already resolved");
181 }
181 }
182 }
182 }
183
183
184 /// <summary>
184 /// <summary>
185 /// Выполняет обещание, сообщая об успешном выполнении. Результатом выполнения будет пустое значения.
185 /// Выполняет обещание, сообщая об успешном выполнении. Результатом выполнения будет пустое значения.
186 /// </summary>
186 /// </summary>
187 /// <remarks>
187 /// <remarks>
188 /// Данный вариант удобен в случаях, когда интересен факт выполнения операции, нежели полученное значение.
188 /// Данный вариант удобен в случаях, когда интересен факт выполнения операции, нежели полученное значение.
189 /// </remarks>
189 /// </remarks>
190 public void Resolve() {
190 public void Resolve() {
191 Resolve(default(T));
191 Resolve(default(T));
192 }
192 }
193
193
194 /// <summary>
194 /// <summary>
195 /// Выполняет обещание, сообщая об ошибке
195 /// Выполняет обещание, сообщая об ошибке
196 /// </summary>
196 /// </summary>
197 /// <remarks>
197 /// <remarks>
198 /// Поскольку обещание должно работать в многопточной среде, при его выполнении сразу несколько потоков
198 /// Поскольку обещание должно работать в многопточной среде, при его выполнении сразу несколько потоков
199 /// могу вернуть ошибку, при этом только первая будет использована в качестве результата, остальные
199 /// могу вернуть ошибку, при этом только первая будет использована в качестве результата, остальные
200 /// будут проигнорированы.
200 /// будут проигнорированы.
201 /// </remarks>
201 /// </remarks>
202 /// <param name="error">Исключение возникшее при выполнении операции</param>
202 /// <param name="error">Исключение возникшее при выполнении операции</param>
203 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception>
203 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception>
204 public void Reject(Exception error) {
204 public void Reject(Exception error) {
205 if (BeginTransit()) {
205 if (BeginTransit()) {
206 m_error = error;
206 m_error = error;
207 CompleteTransit(REJECTED_STATE);
207 CompleteTransit(REJECTED_STATE);
208 OnStateChanged();
208 OnStateChanged();
209 } else {
209 } else {
210 WaitTransition();
210 WaitTransition();
211 if (m_state == SUCCEEDED_STATE)
211 if (m_state == SUCCEEDED_STATE)
212 throw new InvalidOperationException("The promise is already resolved");
212 throw new InvalidOperationException("The promise is already resolved");
213 }
213 }
214 }
214 }
215
215
216 /// <summary>
216 /// <summary>
217 /// Отменяет операцию, если это возможно.
217 /// Отменяет операцию, если это возможно.
218 /// </summary>
218 /// </summary>
219 /// <remarks>Для определения была ли операция отменена следует использовать свойство <see cref="IsCancelled"/>.</remarks>
219 /// <remarks>Для определения была ли операция отменена следует использовать свойство <see cref="IsCancelled"/>.</remarks>
220 public void Cancel() {
220 public void Cancel() {
221 if (m_cancellable && BeginTransit()) {
221 if (m_cancellable && BeginTransit()) {
222 CompleteTransit(CANCELLED_STATE);
222 CompleteTransit(CANCELLED_STATE);
223 OnStateChanged();
223 OnStateChanged();
224 }
224 }
225 }
225 }
226
226
227 public IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error, Action cancel) {
227 public IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error, Action cancel) {
228 if (success == null && error == null && cancel == null)
228 if (success == null && error == null && cancel == null)
229 return this;
229 return this;
230
230
231 var medium = new Promise<T>(this, true);
231 var medium = new Promise<T>(this, true);
232
232
233 AddHandler(success, error, cancel, medium);
233 AddHandler(success, error, cancel, medium);
234
234
235 return medium;
235 return medium;
236 }
236 }
237
237
238 /// <summary>
238 /// <summary>
239 /// Adds new handlers to this promise.
239 /// Adds new handlers to this promise.
240 /// </summary>
240 /// </summary>
241 /// <param name="success">The handler of the successfully completed operation.
241 /// <param name="success">The handler of the successfully completed operation.
242 /// This handler will recieve an operation result as a parameter.</param>
242 /// This handler will recieve an operation result as a parameter.</param>
243 /// <param name="error">Handles an exception that may occur during the operation and returns the value which will be used as the result of the operation.</param>
243 /// <param name="error">Handles an exception that may occur during the operation and returns the value which will be used as the result of the operation.</param>
244 /// <returns>The new promise chained to this one.</returns>
244 /// <returns>The new promise chained to this one.</returns>
245 public IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error) {
245 public IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error) {
246 if (success == null && error == null)
246 if (success == null && error == null)
247 return this;
247 return this;
248
248
249 var medium = new Promise<T>(this, true);
249 var medium = new Promise<T>(this, true);
250
250
251 AddHandler(success, error, null, medium);
251 AddHandler(success, error, null, medium);
252
252
253 return medium;
253 return medium;
254 }
254 }
255
255
256
256
257
257
258
258
259 public IPromise<T> Then(ResultHandler<T> success) {
259 public IPromise<T> Then(ResultHandler<T> success) {
260 if (success == null)
260 if (success == null)
261 return this;
261 return this;
262
262
263 var medium = new Promise<T>(this, true);
263 var medium = new Promise<T>(this, true);
264
264
265 AddHandler(success, null, null, medium);
265 AddHandler(success, null, null, medium);
266
266
267 return medium;
267 return medium;
268 }
268 }
269
269
270 /// <summary>
270 /// <summary>
271 /// Последний обработчик в цепочки обещаний.
271 /// Последний обработчик в цепочки обещаний.
272 /// </summary>
272 /// </summary>
273 /// <param name="success"></param>
273 /// <param name="success"></param>
274 /// <param name="error"></param>
274 /// <param name="error"></param>
275 /// <param name="cancel"></param>
275 /// <param name="cancel"></param>
276 /// <remarks>
276 /// <remarks>
277 /// <para>
277 /// <para>
278 /// Данный метод не создает связанного с текущим обещания и предназначен для окончания
278 /// Данный метод не создает связанного с текущим обещания и предназначен для окончания
279 /// фсинхронной цепочки.
279 /// фсинхронной цепочки.
280 /// </para>
280 /// </para>
281 /// <para>
281 /// <para>
282 /// Если данный метод вызвать несколько раз, либо добавить другие обработчики, то цепочка
282 /// Если данный метод вызвать несколько раз, либо добавить другие обработчики, то цепочка
283 /// не будет одиночной <see cref="IsExclusive"/> и, как следствие, будет невозможна отмена
283 /// не будет одиночной <see cref="IsExclusive"/> и, как следствие, будет невозможна отмена
284 /// всей цепи обещаний снизу (с самого последнего обещания).
284 /// всей цепи обещаний снизу (с самого последнего обещания).
285 /// </para>
285 /// </para>
286 /// </remarks>
286 /// </remarks>
287 public void Last(ResultHandler<T> success, ErrorHandler error, Action cancel) {
287 public void Last(ResultHandler<T> success, ErrorHandler error, Action cancel) {
288 if (success == null && error == null && cancel == null)
288 if (success == null && error == null && cancel == null)
289 return;
289 return;
290
290
291 ErrorHandler<T> errorHandler = null;
291 ErrorHandler<T> errorHandler = null;
292 if (error != null)
292 if (error != null)
293 errorHandler = err => {
293 errorHandler = err => {
294 error(err);
294 error(err);
295 return default(T);
295 return default(T);
296 };
296 };
297 AddHandler(success, errorHandler, cancel, null);
297 AddHandler(success, errorHandler, cancel, null);
298 }
298 }
299
299
300 public void Last(ResultHandler<T> success, ErrorHandler error) {
300 public void Last(ResultHandler<T> success, ErrorHandler error) {
301 Last(success, error, null);
301 Last(success, error, null);
302 }
302 }
303
303
304 public void Last(ResultHandler<T> success) {
304 public void Last(ResultHandler<T> success) {
305 Last(success, null, null);
305 Last(success, null, null);
306 }
306 }
307
307
308 public IPromise Error(ErrorHandler error) {
308 public IPromise Error(ErrorHandler error) {
309 if (error == null)
309 if (error == null)
310 return this;
310 return this;
311
311
312 var medium = new Promise<T>(this, true);
312 var medium = new Promise<T>(this, true);
313
313
314 AddHandler(
314 AddHandler(
315 null,
315 null,
316 e => {
316 e => {
317 error(e);
317 error(e);
318 return default(T);
318 return default(T);
319 },
319 },
320 null,
320 null,
321 medium
321 medium
322 );
322 );
323
323
324 return medium;
324 return medium;
325 }
325 }
326
326
327 /// <summary>
327 /// <summary>
328 /// Handles error and allows to keep the promise.
328 /// Handles error and allows to keep the promise.
329 /// </summary>
329 /// </summary>
330 /// <remarks>
330 /// <remarks>
331 /// If the specified handler throws an exception, this exception will be used to reject the promise.
331 /// If the specified handler throws an exception, this exception will be used to reject the promise.
332 /// </remarks>
332 /// </remarks>
333 /// <param name="handler">The error handler which returns the result of the promise.</param>
333 /// <param name="handler">The error handler which returns the result of the promise.</param>
334 /// <returns>New promise.</returns>
334 /// <returns>New promise.</returns>
335 public IPromise<T> Error(ErrorHandler<T> handler) {
335 public IPromise<T> Error(ErrorHandler<T> handler) {
336 if (handler == null)
336 if (handler == null)
337 return this;
337 return this;
338
338
339 var medium = new Promise<T>(this, true);
339 var medium = new Promise<T>(this, true);
340
340
341 AddHandler(null, handler, null, medium);
341 AddHandler(null, handler, null, medium);
342
342
343 return medium;
343 return medium;
344 }
344 }
345
345
346 /// <summary>
346 /// <summary>
347 /// Позволяет преобразовать результат выполения операции к новому типу.
347 /// Позволяет преобразовать результат выполения операции к новому типу.
348 /// </summary>
348 /// </summary>
349 /// <typeparam name="TNew">Новый тип результата.</typeparam>
349 /// <typeparam name="TNew">Новый тип результата.</typeparam>
350 /// <param name="mapper">Преобразование результата к новому типу.</param>
350 /// <param name="mapper">Преобразование результата к новому типу.</param>
351 /// <param name="error">Обработчик ошибки. Данный обработчик получит
351 /// <param name="error">Обработчик ошибки. Данный обработчик получит
352 /// исключение возникшее при выполнении операции.</param>
352 /// исключение возникшее при выполнении операции.</param>
353 /// <returns>Новое обещание, которое будет выполнено при выполнении исходного обещания.</returns>
353 /// <returns>Новое обещание, которое будет выполнено при выполнении исходного обещания.</returns>
354 /// <param name = "cancel"></param>
354 public IPromise<TNew> Then<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler<TNew> error, Action cancel) {
355 public IPromise<TNew> Then<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler<TNew> error, Action cancel) {
355 Safe.ArgumentNotNull(mapper, "mapper");
356 Safe.ArgumentNotNull(mapper, "mapper");
356
357
357 // создаем прицепленное обещание
358 // создаем прицепленное обещание
358 var medium = new Promise<TNew>(this, true);
359 var medium = new Promise<TNew>(this, true);
359
360
360 ResultHandler<T> resultHandler = result => medium.Resolve(mapper(result));
361 ResultHandler<T> resultHandler = result => medium.Resolve(mapper(result));
361 ErrorHandler<T> errorHandler;
362 ErrorHandler<T> errorHandler;
362 if (error != null)
363 if (error != null)
363 errorHandler = e => {
364 errorHandler = e => {
364 try {
365 try {
365 medium.Resolve(error(e));
366 medium.Resolve(error(e));
366 } catch (Exception e2) {
367 } catch (Exception e2) {
367 // в случае ошибки нужно передать исключение дальше по цепочке
368 // в случае ошибки нужно передать исключение дальше по цепочке
368 medium.Reject(e2);
369 medium.Reject(e2);
369 }
370 }
370 return default(T);
371 return default(T);
371 };
372 };
372 else
373 else
373 errorHandler = e => {
374 errorHandler = e => {
374 medium.Reject(e);
375 medium.Reject(e);
375 return default(T);
376 return default(T);
376 };
377 };
377
378
378 Action cancelHandler;
379 Action cancelHandler;
379 if (cancel != null)
380 if (cancel != null)
380 cancelHandler = () => {
381 cancelHandler = () => {
381 cancel();
382 cancel();
382 medium.Cancel();
383 medium.Cancel();
383 };
384 };
384 else
385 else
385 cancelHandler = medium.Cancel;
386 cancelHandler = medium.Cancel;
386
387
387
388
388 AddHandler(
389 AddHandler(
389 resultHandler,
390 resultHandler,
390 errorHandler,
391 errorHandler,
391 cancelHandler,
392 cancelHandler,
392 null
393 null
393 );
394 );
394
395
395 return medium;
396 return medium;
396 }
397 }
397
398
398 public IPromise<TNew> Then<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler<TNew> error) {
399 public IPromise<TNew> Then<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler<TNew> error) {
399 return Then(mapper, error, null);
400 return Then(mapper, error, null);
400 }
401 }
401
402
402 public IPromise<TNew> Then<TNew>(ResultMapper<T, TNew> mapper) {
403 public IPromise<TNew> Then<TNew>(ResultMapper<T, TNew> mapper) {
403 return Then(mapper, null, null);
404 return Then(mapper, null, null);
404 }
405 }
405
406
406 /// <summary>
407 /// <summary>
407 /// Сцепляет несколько аснхронных операций. Указанная асинхронная операция будет вызвана после
408 /// Сцепляет несколько аснхронных операций. Указанная асинхронная операция будет вызвана после
408 /// выполнения текущей, а результат текущей операции может быть использован для инициализации
409 /// выполнения текущей, а результат текущей операции может быть использован для инициализации
409 /// новой операции.
410 /// новой операции.
410 /// </summary>
411 /// </summary>
411 /// <typeparam name="TNew">Тип результата указанной асинхронной операции.</typeparam>
412 /// <typeparam name="TNew">Тип результата указанной асинхронной операции.</typeparam>
412 /// <param name="chained">Асинхронная операция, которая должна будет начаться после выполнения текущей.</param>
413 /// <param name="chained">Асинхронная операция, которая должна будет начаться после выполнения текущей.</param>
413 /// <param name="error">Обработчик ошибки. Данный обработчик получит
414 /// <param name="error">Обработчик ошибки. Данный обработчик получит
414 /// исключение возникшее при выполнении текуещй операции.</param>
415 /// исключение возникшее при выполнении текуещй операции.</param>
415 /// <returns>Новое обещание, которое будет выполнено по окончанию указанной аснхронной операции.</returns>
416 /// <returns>Новое обещание, которое будет выполнено по окончанию указанной аснхронной операции.</returns>
417 /// <param name = "cancel"></param>
416 public IPromise<TNew> Chain<TNew>(ResultMapper<T, IPromise<TNew>> chained, ErrorHandler<IPromise<TNew>> error, Action cancel) {
418 public IPromise<TNew> Chain<TNew>(ResultMapper<T, IPromise<TNew>> chained, ErrorHandler<IPromise<TNew>> error, Action cancel) {
417
419
418 Safe.ArgumentNotNull(chained, "chained");
420 Safe.ArgumentNotNull(chained, "chained");
419
421
420 // проблема в том, что на момент связывания еще не начата асинхронная операция, поэтому нужно
422 // проблема в том, что на момент связывания еще не начата асинхронная операция, поэтому нужно
421 // создать посредника, к которому будут подвызяваться следующие обработчики.
423 // создать посредника, к которому будут подвызяваться следующие обработчики.
422 // когда будет выполнена реальная асинхронная операция, она обратиться к посреднику, чтобы
424 // когда будет выполнена реальная асинхронная операция, она обратиться к посреднику, чтобы
423 // передать через него результаты работы.
425 // передать через него результаты работы.
424 var medium = new Promise<TNew>(this, true);
426 var medium = new Promise<TNew>(this, true);
425
427
426 ResultHandler<T> resultHandler = delegate(T result) {
428 ResultHandler<T> resultHandler = delegate(T result) {
427 if (medium.IsCancelled)
429 if (medium.IsCancelled)
428 return;
430 return;
429
431
430 var promise = chained(result);
432 var promise = chained(result);
431
433
432 promise.Last(
434 promise.Last(
433 medium.Resolve,
435 medium.Resolve,
434 medium.Reject,
436 medium.Reject,
435 () => medium.Reject(new OperationCanceledException()) // внешняя отмена связанной операции рассматривается как ошибка
437 () => medium.Reject(new OperationCanceledException()) // внешняя отмена связанной операции рассматривается как ошибка
436 );
438 );
437
439
438 // notify chained operation that it's not needed anymore
440 // notify chained operation that it's not needed anymore
439 // порядок вызова Then, Cancelled важен, поскольку от этого
441 // порядок вызова Then, Cancelled важен, поскольку от этого
440 // зависит IsExclusive
442 // зависит IsExclusive
441 medium.Cancelled(() => {
443 medium.Cancelled(() => {
442 if (promise.IsExclusive)
444 if (promise.IsExclusive)
443 promise.Cancel();
445 promise.Cancel();
444 });
446 });
445 };
447 };
446
448
447 ErrorHandler<T> errorHandler;
449 ErrorHandler<T> errorHandler;
448
450
449 if (error != null)
451 if (error != null)
450 errorHandler = delegate(Exception e) {
452 errorHandler = delegate(Exception e) {
451 try {
453 try {
452 var promise = error(e);
454 var promise = error(e);
453
455
454 promise.Last(
456 promise.Last(
455 medium.Resolve,
457 medium.Resolve,
456 medium.Reject,
458 medium.Reject,
457 () => medium.Reject(new OperationCanceledException()) // внешняя отмена связанной операции рассматривается как ошибка
459 () => medium.Reject(new OperationCanceledException()) // внешняя отмена связанной операции рассматривается как ошибка
458 );
460 );
459
461
460 // notify chained operation that it's not needed anymore
462 // notify chained operation that it's not needed anymore
461 // порядок вызова Then, Cancelled важен, поскольку от этого
463 // порядок вызова Then, Cancelled важен, поскольку от этого
462 // зависит IsExclusive
464 // зависит IsExclusive
463 medium.Cancelled(() => {
465 medium.Cancelled(() => {
464 if (promise.IsExclusive)
466 if (promise.IsExclusive)
465 promise.Cancel();
467 promise.Cancel();
466 });
468 });
467 } catch (Exception e2) {
469 } catch (Exception e2) {
468 medium.Reject(e2);
470 medium.Reject(e2);
469 }
471 }
470 return default(T);
472 return default(T);
471 };
473 };
472 else
474 else
473 errorHandler = err => {
475 errorHandler = err => {
474 medium.Reject(err);
476 medium.Reject(err);
475 return default(T);
477 return default(T);
476 };
478 };
477
479
478
480
479 Action cancelHandler;
481 Action cancelHandler;
480 if (cancel != null)
482 if (cancel != null)
481 cancelHandler = () => {
483 cancelHandler = () => {
482 if (cancel != null)
484 if (cancel != null)
483 cancel();
485 cancel();
484 medium.Cancel();
486 medium.Cancel();
485 };
487 };
486 else
488 else
487 cancelHandler = medium.Cancel;
489 cancelHandler = medium.Cancel;
488
490
489 AddHandler(
491 AddHandler(
490 resultHandler,
492 resultHandler,
491 errorHandler,
493 errorHandler,
492 cancelHandler,
494 cancelHandler,
493 null
495 null
494 );
496 );
495
497
496 return medium;
498 return medium;
497 }
499 }
498
500
499 public IPromise<TNew> Chain<TNew>(ResultMapper<T, IPromise<TNew>> chained, ErrorHandler<IPromise<TNew>> error) {
501 public IPromise<TNew> Chain<TNew>(ResultMapper<T, IPromise<TNew>> chained, ErrorHandler<IPromise<TNew>> error) {
500 return Chain(chained, error, null);
502 return Chain(chained, error, null);
501 }
503 }
502
504
503 public IPromise<TNew> Chain<TNew>(ResultMapper<T, IPromise<TNew>> chained) {
505 public IPromise<TNew> Chain<TNew>(ResultMapper<T, IPromise<TNew>> chained) {
504 return Chain(chained, null, null);
506 return Chain(chained, null, null);
505 }
507 }
506
508
507 public IPromise<T> Cancelled(Action handler) {
509 public IPromise<T> Cancelled(Action handler) {
508 var medium = new Promise<T>(this,true);
510 var medium = new Promise<T>(this,true);
509 AddHandler(null, null, handler, medium);
511 AddHandler(null, null, handler, medium);
510 return medium;
512 return medium;
511 }
513 }
512
514
513 /// <summary>
515 /// <summary>
514 /// Adds the specified handler for all cases (success, error, cancel)
516 /// Adds the specified handler for all cases (success, error, cancel)
515 /// </summary>
517 /// </summary>
516 /// <param name="handler">The handler that will be called anyway</param>
518 /// <param name="handler">The handler that will be called anyway</param>
517 /// <returns>self</returns>
519 /// <returns>self</returns>
518 public IPromise<T> Anyway(Action handler) {
520 public IPromise<T> Anyway(Action handler) {
519 Safe.ArgumentNotNull(handler, "handler");
521 Safe.ArgumentNotNull(handler, "handler");
520
522
521 AddHandler(
523 AddHandler(
522 x => handler(),
524 x => handler(),
523 e => {
525 e => {
524 handler();
526 handler();
525 throw new TransientPromiseException(e);
527 throw new TransientPromiseException(e);
526 },
528 },
527 handler,
529 handler,
528 null
530 null
529 );
531 );
530 return this;
532 return this;
531 }
533 }
532
534
533 /// <summary>
535 /// <summary>
534 /// Преобразует результат обещания к нужному типу
536 /// Преобразует результат обещания к нужному типу
535 /// </summary>
537 /// </summary>
536 /// <typeparam name="T2"></typeparam>
538 /// <typeparam name="T2"></typeparam>
537 /// <returns></returns>
539 /// <returns></returns>
538 public IPromise<T2> Cast<T2>() {
540 public IPromise<T2> Cast<T2>() {
539 return Then(x => (T2)(object)x, null);
541 return Then(x => (T2)(object)x, null);
540 }
542 }
541
543
542 /// <summary>
544 /// <summary>
543 /// Дожидается отложенного обещания и в случае успеха, возвращает
545 /// Дожидается отложенного обещания и в случае успеха, возвращает
544 /// его, результат, в противном случае бросает исключение.
546 /// его, результат, в противном случае бросает исключение.
545 /// </summary>
547 /// </summary>
546 /// <remarks>
548 /// <remarks>
547 /// <para>
549 /// <para>
548 /// Если ожидание обещания было прервано по таймауту, это не значит,
550 /// Если ожидание обещания было прервано по таймауту, это не значит,
549 /// что обещание было отменено или что-то в этом роде, это только
551 /// что обещание было отменено или что-то в этом роде, это только
550 /// означает, что мы его не дождались, однако все зарегистрированные
552 /// означает, что мы его не дождались, однако все зарегистрированные
551 /// обработчики, как были так остались и они будут вызваны, когда
553 /// обработчики, как были так остались и они будут вызваны, когда
552 /// обещание будет выполнено.
554 /// обещание будет выполнено.
553 /// </para>
555 /// </para>
554 /// <para>
556 /// <para>
555 /// Такое поведение вполне оправдано поскольку таймаут может истечь
557 /// Такое поведение вполне оправдано поскольку таймаут может истечь
556 /// в тот момент, когда началась обработка цепочки обработчиков, и
558 /// в тот момент, когда началась обработка цепочки обработчиков, и
557 /// к тому же текущее обещание может стоять в цепочке обещаний и его
559 /// к тому же текущее обещание может стоять в цепочке обещаний и его
558 /// отклонение может привести к непрогнозируемому результату.
560 /// отклонение может привести к непрогнозируемому результату.
559 /// </para>
561 /// </para>
560 /// </remarks>
562 /// </remarks>
561 /// <param name="timeout">Время ожидания</param>
563 /// <param name="timeout">Время ожидания</param>
562 /// <returns>Результат выполнения обещания</returns>
564 /// <returns>Результат выполнения обещания</returns>
563 public T Join(int timeout) {
565 public T Join(int timeout) {
564 var evt = new ManualResetEvent(false);
566 var evt = new ManualResetEvent(false);
565 Anyway(() => evt.Set());
567 Anyway(() => evt.Set());
566
568
567 if (!evt.WaitOne(timeout, true))
569 if (!evt.WaitOne(timeout, true))
568 throw new TimeoutException();
570 throw new TimeoutException();
569
571
570 switch (m_state) {
572 switch (m_state) {
571 case SUCCEEDED_STATE:
573 case SUCCEEDED_STATE:
572 return m_result;
574 return m_result;
573 case CANCELLED_STATE:
575 case CANCELLED_STATE:
574 throw new OperationCanceledException();
576 throw new OperationCanceledException();
575 case REJECTED_STATE:
577 case REJECTED_STATE:
576 throw new TargetInvocationException(m_error);
578 throw new TargetInvocationException(m_error);
577 default:
579 default:
578 throw new ApplicationException(String.Format("Invalid promise state {0}", m_state));
580 throw new ApplicationException(String.Format("Invalid promise state {0}", m_state));
579 }
581 }
580 }
582 }
581
583
582 public T Join() {
584 public T Join() {
583 return Join(Timeout.Infinite);
585 return Join(Timeout.Infinite);
584 }
586 }
585
587
586 void AddHandler(ResultHandler<T> success, ErrorHandler<T> error, Action cancel, Promise<T> medium) {
588 void AddHandler(ResultHandler<T> success, ErrorHandler<T> error, Action cancel, Promise<T> medium) {
587 if (success != null || error != null)
589 if (success != null || error != null)
588 Interlocked.Increment(ref m_childrenCount);
590 Interlocked.Increment(ref m_childrenCount);
589
591
590 var handler = new HandlerDescriptor {
592 var handler = new HandlerDescriptor {
591 resultHandler = success,
593 resultHandler = success,
592 errorHandler = error,
594 errorHandler = error,
593 cancellHandler = cancel,
595 cancellHandler = cancel,
594 medium = medium
596 medium = medium
595 };
597 };
596
598
597 bool queued;
599 bool queued;
598
600
599 if (!IsResolved) {
601 if (!IsResolved) {
600 m_handlers.Enqueue(handler);
602 m_handlers.Enqueue(handler);
601 queued = true;
603 queued = true;
602 } else {
604 } else {
603 // the promise is in resolved state, just invoke the handled with minimum overhead
605 // the promise is in resolved state, just invoke the handled with minimum overhead
604 queued = false;
606 queued = false;
605 InvokeHandler(handler);
607 InvokeHandler(handler);
606 }
608 }
607
609
608 if (queued && IsResolved && m_handlers.TryDequeue(out handler))
610 if (queued && IsResolved && m_handlers.TryDequeue(out handler))
609 // if the promise have been resolved while we was adding handler to the queue
611 // if the promise have been resolved while we was adding handler to the queue
610 // we can't guarantee that someone is still processing it
612 // we can't guarantee that someone is still processing it
611 // therefore we will fetch a handler from the queue and execute it
613 // therefore we will fetch a handler from the queue and execute it
612 // note that fetched handler may be not the one that we have added
614 // note that fetched handler may be not the one that we have added
613 // even we can fetch no handlers at all :)
615 // even we can fetch no handlers at all :)
614 InvokeHandler(handler);
616 InvokeHandler(handler);
615 }
617 }
616
618
617 protected virtual void InvokeHandler(HandlerDescriptor handler) {
619 protected virtual void InvokeHandler(HandlerDescriptor handler) {
618 switch (m_state) {
620 switch (m_state) {
619 case SUCCEEDED_STATE:
621 case SUCCEEDED_STATE:
620 handler.Resolve(m_result);
622 handler.Resolve(m_result);
621 break;
623 break;
622 case REJECTED_STATE:
624 case REJECTED_STATE:
623 handler.Reject(m_error);
625 handler.Reject(m_error);
624 break;
626 break;
625 case CANCELLED_STATE:
627 case CANCELLED_STATE:
626 handler.Cancel();
628 handler.Cancel();
627 break;
629 break;
628 default:
630 default:
629 // do nothing
631 // do nothing
630 return;
632 return;
631 }
633 }
632 }
634 }
633
635
634 void OnStateChanged() {
636 void OnStateChanged() {
635 HandlerDescriptor handler;
637 HandlerDescriptor handler;
636 while (m_handlers.TryDequeue(out handler))
638 while (m_handlers.TryDequeue(out handler))
637 InvokeHandler(handler);
639 InvokeHandler(handler);
638 }
640 }
639
641
640 public bool IsExclusive {
642 public bool IsExclusive {
641 get {
643 get {
642 return m_childrenCount <= 1;
644 return m_childrenCount <= 1;
643 }
645 }
644 }
646 }
645
647
646 /// <summary>
648 /// <summary>
647 /// Объединяет несколько обещаний в одно, результатом которого является массив результатов других обещаний.
649 /// Объединяет несколько обещаний в одно, результатом которого является массив результатов других обещаний.
648 /// Если хотябы одно из переданных обещаний не будет выполнено, то новое обещение тоже не будет выполнено.
650 /// Если хотябы одно из переданных обещаний не будет выполнено, то новое обещение тоже не будет выполнено.
649 /// При отмене нового обещания, переданные обещания также будут отменены, если никто больше на них не подписан.
651 /// При отмене нового обещания, переданные обещания также будут отменены, если никто больше на них не подписан.
650 /// </summary>
652 /// </summary>
651 /// <param name="promises">Список обещаний. Если список пустой, то результирующее обещание возвращается уже выполненным.</param>
653 /// <param name="promises">Список обещаний. Если список пустой, то результирующее обещание возвращается уже выполненным.</param>
652 /// <returns>Обещание объединяющее в себе результат переданных обещаний.</returns>
654 /// <returns>Обещание объединяющее в себе результат переданных обещаний.</returns>
653 /// <exception cref="ArgumentNullException"><paramref name="promises"/> не может быть null</exception>
655 /// <exception cref="ArgumentNullException"><paramref name="promises"/> не может быть null</exception>
654 public static IPromise<T[]> CreateComposite(IList<IPromise<T>> promises) {
656 public static IPromise<T[]> CreateComposite(IList<IPromise<T>> promises) {
655 if (promises == null)
657 if (promises == null)
656 throw new ArgumentNullException();
658 throw new ArgumentNullException();
657
659
658 // создаем аккумулятор для результатов и результирующее обещание
660 // создаем аккумулятор для результатов и результирующее обещание
659 var result = new T[promises.Count];
661 var result = new T[promises.Count];
660 var promise = new Promise<T[]>();
662 var promise = new Promise<T[]>();
661
663
662 // special case
664 // special case
663 if (promises.Count == 0) {
665 if (promises.Count == 0) {
664 promise.Resolve(result);
666 promise.Resolve(result);
665 return promise;
667 return promise;
666 }
668 }
667
669
668 int pending = promises.Count;
670 int pending = promises.Count;
669
671
670 for (int i = 0; i < promises.Count; i++) {
672 for (int i = 0; i < promises.Count; i++) {
671 var dest = i;
673 var dest = i;
672
674
673 if (promises[i] != null) {
675 if (promises[i] != null) {
674 promises[i].Then(
676 promises[i].Then(
675 x => {
677 x => {
676 result[dest] = x;
678 result[dest] = x;
677 if (Interlocked.Decrement(ref pending) == 0)
679 if (Interlocked.Decrement(ref pending) == 0)
678 promise.Resolve(result);
680 promise.Resolve(result);
679 },
681 },
680 e => {
682 e => {
681 promise.Reject(e);
683 promise.Reject(e);
682 return default(T);
684 return default(T);
683 }
685 }
684 );
686 );
685 } else {
687 } else {
686 if (Interlocked.Decrement(ref pending) == 0)
688 if (Interlocked.Decrement(ref pending) == 0)
687 promise.Resolve(result);
689 promise.Resolve(result);
688 }
690 }
689 }
691 }
690
692
691 promise.Cancelled(
693 promise.Cancelled(
692 () => {
694 () => {
693 foreach (var d in promises)
695 foreach (var d in promises)
694 if (d != null && d.IsExclusive)
696 if (d != null && d.IsExclusive)
695 d.Cancel();
697 d.Cancel();
696 }
698 }
697 );
699 );
698
700
699 return promise;
701 return promise;
700 }
702 }
701
703
702 /// <summary>
704 /// <summary>
703 /// Объединяет несколько обещаний в одно. Результирующее обещание будет выполнено при
705 /// Объединяет несколько обещаний в одно. Результирующее обещание будет выполнено при
704 /// выполнении всех указанных обещаний. При этом возвращаемые значения первичных обещаний
706 /// выполнении всех указанных обещаний. При этом возвращаемые значения первичных обещаний
705 /// игнорируются.
707 /// игнорируются.
706 /// </summary>
708 /// </summary>
707 /// <param name="promises">Коллекция первичных обещаний, которые будут объеденены в одно.</param>
709 /// <param name="promises">Коллекция первичных обещаний, которые будут объеденены в одно.</param>
708 /// <returns>Новое обещание, объединяющее в себе переданные.</returns>
710 /// <returns>Новое обещание, объединяющее в себе переданные.</returns>
709 /// <remarks>
711 /// <remarks>
710 /// Если в коллекции встречаюься <c>null</c>, то они воспринимаются как выполненные обещания.
712 /// Если в коллекции встречаюься <c>null</c>, то они воспринимаются как выполненные обещания.
711 /// </remarks>
713 /// </remarks>
712 public static IPromise CreateComposite(ICollection<IPromise> promises) {
714 public static IPromise CreateComposite(ICollection<IPromise> promises) {
713 if (promises == null)
715 if (promises == null)
714 throw new ArgumentNullException();
716 throw new ArgumentNullException();
715 if (promises.Count == 0)
717 if (promises.Count == 0)
716 return Promise<object>.ResultToPromise(null);
718 return Promise<object>.ResultToPromise(null);
717
719
718 int countdown = promises.Count;
720 int countdown = promises.Count;
719
721
720 var result = new Promise<object>();
722 var result = new Promise<object>();
721
723
722 foreach (var d in promises) {
724 foreach (var d in promises) {
723 if (d == null) {
725 if (d == null) {
724 if (Interlocked.Decrement(ref countdown) == 0)
726 if (Interlocked.Decrement(ref countdown) == 0)
725 result.Resolve(null);
727 result.Resolve(null);
726 } else {
728 } else {
727 d.Then(() => {
729 d.Then(() => {
728 if (Interlocked.Decrement(ref countdown) == 0)
730 if (Interlocked.Decrement(ref countdown) == 0)
729 result.Resolve(null);
731 result.Resolve(null);
730 });
732 });
731 }
733 }
732 }
734 }
733
735
734 result.Cancelled(() => {
736 result.Cancelled(() => {
735 foreach (var d in promises)
737 foreach (var d in promises)
736 if (d != null && d.IsExclusive)
738 if (d != null && d.IsExclusive)
737 d.Cancel();
739 d.Cancel();
738 });
740 });
739
741
740 return result;
742 return result;
741 }
743 }
742
744
743 public static Promise<T> ResultToPromise(T result) {
745 public static Promise<T> ResultToPromise(T result) {
744 var p = new Promise<T>();
746 var p = new Promise<T>();
745 p.Resolve(result);
747 p.Resolve(result);
746 return p;
748 return p;
747 }
749 }
748
750
749 public static Promise<T> ExceptionToPromise(Exception error) {
751 public static Promise<T> ExceptionToPromise(Exception error) {
750 if (error == null)
752 if (error == null)
751 throw new ArgumentNullException();
753 throw new ArgumentNullException();
752
754
753 var p = new Promise<T>();
755 var p = new Promise<T>();
754 p.Reject(error);
756 p.Reject(error);
755 return p;
757 return p;
756 }
758 }
757
759
758 #region IPromiseBase explicit implementation
760 #region IPromiseBase explicit implementation
759
761
760 IPromise IPromise.Then(Action success, ErrorHandler error, Action cancel) {
762 IPromise IPromise.Then(Action success, ErrorHandler error, Action cancel) {
761 return Then(
763 return Then(
762 x => success(),
764 success != null ? new ResultHandler<T>(x => success()) : null,
763 e => {
765 error != null ? new ErrorHandler<T>(e => {
764 error(e);
766 error(e);
765 return default(T);
767 return default(T);
766 },
768 }) : null,
767 cancel
769 cancel
768 );
770 );
769 }
771 }
770
772
771 IPromise IPromise.Then(Action success, ErrorHandler error) {
773 IPromise IPromise.Then(Action success, ErrorHandler error) {
772 return Then(
774 return Then(
773 x => success(),
775 success != null ? new ResultHandler<T>(x => success()) : null,
774 e => {
776 error != null ? new ErrorHandler<T>(e => {
775 error(e);
777 error(e);
776 return default(T);
778 return default(T);
777 }
779 }) : null
778 );
780 );
779 }
781 }
780
782
781 IPromise IPromise.Then(Action success) {
783 IPromise IPromise.Then(Action success) {
784 Safe.ArgumentNotNull(success, "success");
782 return Then(x => success());
785 return Then(x => success());
783 }
786 }
784
787
788 IPromise IPromise.Chain(Func<IPromise> chained, ErrorHandler<IPromise> error, Action cancel) {
789 return ChainNoResult(chained, error, cancel);
790 }
791
792 IPromise ChainNoResult(Func<IPromise> chained, ErrorHandler<IPromise> error, Action cancel) {
793 Safe.ArgumentNotNull(chained, "chained");
794
795 var medium = new Promise<object>(this, true);
796
797 ResultHandler<T> resultHandler = delegate(T result) {
798 if (medium.IsCancelled)
799 return;
800
801 var promise = chained();
802
803 promise.Last(
804 medium.Resolve,
805 medium.Reject,
806 () => medium.Reject(new OperationCanceledException()) // внешняя отмена связанной операции рассматривается как ошибка
807 );
808
809 // notify chained operation that it's not needed anymore
810 // порядок вызова Then, Cancelled важен, поскольку от этого
811 // зависит IsExclusive
812 medium.Cancelled(() => {
813 if (promise.IsExclusive)
814 promise.Cancel();
815 });
816 };
817
818 ErrorHandler<T> errorHandler;
819
820 if (error != null)
821 errorHandler = delegate(Exception e) {
822 try {
823 var promise = error(e);
824
825 promise.Last(
826 medium.Resolve,
827 medium.Reject,
828 () => medium.Reject(new OperationCanceledException()) // внешняя отмена связанной операции рассматривается как ошибка
829 );
830
831 // notify chained operation that it's not needed anymore
832 // порядок вызова Then, Cancelled важен, поскольку от этого
833 // зависит IsExclusive
834 medium.Cancelled(() => {
835 if (promise.IsExclusive)
836 promise.Cancel();
837 });
838 } catch (Exception e2) {
839 medium.Reject(e2);
840 }
841 return default(T);
842 };
843 else
844 errorHandler = err => {
845 medium.Reject(err);
846 return default(T);
847 };
848
849
850 Action cancelHandler;
851 if (cancel != null)
852 cancelHandler = () => {
853 if (cancel != null)
854 cancel();
855 medium.Cancel();
856 };
857 else
858 cancelHandler = medium.Cancel;
859
860 AddHandler(
861 resultHandler,
862 errorHandler,
863 cancelHandler,
864 null
865 );
866
867 return medium;
868 }
869 IPromise IPromise.Chain(Func<IPromise> chained, ErrorHandler<IPromise> error) {
870 return ChainNoResult(chained, error, null);
871 }
872 IPromise IPromise.Chain(Func<IPromise> chained) {
873 return ChainNoResult(chained, null, null);
874 }
875
876
785 void IPromise.Last(Action success, ErrorHandler error, Action cancel) {
877 void IPromise.Last(Action success, ErrorHandler error, Action cancel) {
786 Last(x => success(), error, cancel);
878 Last(x => success(), error, cancel);
787 }
879 }
788
880
789 void IPromise.Last(Action success, ErrorHandler error) {
881 void IPromise.Last(Action success, ErrorHandler error) {
790 Last(x => success(), error, null);
882 Last(x => success(), error, null);
791 }
883 }
792
884
793 void IPromise.Last(Action success) {
885 void IPromise.Last(Action success) {
794 Last(x => success(), null, null);
886 Last(x => success(), null, null);
795 }
887 }
796
888
797 IPromise IPromise.Error(ErrorHandler error) {
889 IPromise IPromise.Error(ErrorHandler error) {
798 return Error(error);
890 return Error(error);
799 }
891 }
800
892
801 IPromise IPromise.Anyway(Action handler) {
893 IPromise IPromise.Anyway(Action handler) {
802 return Anyway(handler);
894 return Anyway(handler);
803 }
895 }
804
896
805 IPromise IPromise.Cancelled(Action handler) {
897 IPromise IPromise.Cancelled(Action handler) {
806 return Cancelled(handler);
898 return Cancelled(handler);
807 }
899 }
808
900
809 void IPromise.Join() {
901 void IPromise.Join() {
810 Join();
902 Join();
811 }
903 }
812
904
813 void IPromise.Join(int timeout) {
905 void IPromise.Join(int timeout) {
814 Join(timeout);
906 Join(timeout);
815 }
907 }
816
908
817 #endregion
909 #endregion
818
910
819
911
820
912
821 }
913 }
822 }
914 }
@@ -1,86 +1,86
1 using System;
1 using System;
2 using System.Collections.Generic;
2 using System.Collections.Generic;
3 using System.Linq;
3 using System.Linq;
4 using System.Text;
4 using System.Text;
5 using System.Text.RegularExpressions;
5 using System.Text.RegularExpressions;
6 using System.Diagnostics;
6 using System.Diagnostics;
7
7
8 namespace Implab
8 namespace Implab
9 {
9 {
10 public static class Safe
10 public static class Safe
11 {
11 {
12 public static void ArgumentMatch(string param, string name, Regex rx) {
12 public static void ArgumentMatch(string param, string name, Regex rx) {
13 if (rx == null)
13 if (rx == null)
14 throw new ArgumentNullException("rx");
14 throw new ArgumentNullException("rx");
15 if (!rx.IsMatch(param))
15 if (!rx.IsMatch(param))
16 throw new ArgumentException(String.Format("The prameter value must match {0}", rx), name);
16 throw new ArgumentException(String.Format("The prameter value must match {0}", rx), name);
17 }
17 }
18
18
19 public static void ArgumentNotEmpty(string param, string name) {
19 public static void ArgumentNotEmpty(string param, string name) {
20 if (String.IsNullOrEmpty(param))
20 if (String.IsNullOrEmpty(param))
21 throw new ArgumentException("The parameter can't be empty", name);
21 throw new ArgumentException("The parameter can't be empty", name);
22 }
22 }
23
23
24 public static void ArgumentNotEmpty<T>(T[] param, string name) {
24 public static void ArgumentNotEmpty<T>(T[] param, string name) {
25 if (param == null || param.Length == 0)
25 if (param == null || param.Length == 0)
26 throw new ArgumentException("The array must be not emty");
26 throw new ArgumentException("The array must be not emty");
27 }
27 }
28
28
29 public static void ArgumentNotNull(object param, string name) {
29 public static void ArgumentNotNull(object param, string name) {
30 if (param == null)
30 if (param == null)
31 throw new ArgumentNullException(name);
31 throw new ArgumentNullException(name);
32 }
32 }
33
33
34 public static void ArgumentInRange(int arg, int min, int max, string name) {
34 public static void ArgumentInRange(int arg, int min, int max, string name) {
35 if (arg < min || arg > max)
35 if (arg < min || arg > max)
36 throw new ArgumentOutOfRangeException(name);
36 throw new ArgumentOutOfRangeException(name);
37 }
37 }
38
38
39 public static void Dispose<T>(T obj) where T : class
39 public static void Dispose<T>(T obj) where T : class
40 {
40 {
41 var disp = obj as IDisposable;
41 var disp = obj as IDisposable;
42 if (disp != null)
42 if (disp != null)
43 disp.Dispose();
43 disp.Dispose();
44 }
44 }
45
45
46 [DebuggerStepThrough]
46 [DebuggerStepThrough]
47 public static IPromise<T> InvokePromise<T>(Func<T> action) {
47 public static IPromise<T> InvokePromise<T>(Func<T> action) {
48 ArgumentNotNull(action, "action");
48 ArgumentNotNull(action, "action");
49
49
50 var p = new Promise<T>();
50 var p = new Promise<T>();
51 try {
51 try {
52 p.Resolve(action());
52 p.Resolve(action());
53 } catch (Exception err) {
53 } catch (Exception err) {
54 p.Reject(err);
54 p.Reject(err);
55 }
55 }
56
56
57 return p;
57 return p;
58 }
58 }
59
59
60 [DebuggerStepThrough]
60 [DebuggerStepThrough]
61 public static IPromise<T> InvokePromise<T>(Action action) {
61 public static IPromise InvokePromise(Action action) {
62 ArgumentNotNull(action, "action");
62 ArgumentNotNull(action, "action");
63
63
64 var p = new Promise<object>();
64 var p = new Promise<object>();
65 try {
65 try {
66 action();
66 action();
67 p.Resolve();
67 p.Resolve();
68 } catch (Exception err) {
68 } catch (Exception err) {
69 p.Reject(err);
69 p.Reject(err);
70 }
70 }
71
71
72 return p;
72 return p;
73 }
73 }
74
74
75 [DebuggerStepThrough]
75 [DebuggerStepThrough]
76 public static IPromise<T> InvokePromise<T>(Func<IPromise<T>> action) {
76 public static IPromise<T> InvokePromise<T>(Func<IPromise<T>> action) {
77 ArgumentNotNull(action, "action");
77 ArgumentNotNull(action, "action");
78
78
79 try {
79 try {
80 return action() ?? Promise<T>.ExceptionToPromise(new Exception("The action returned null"));
80 return action() ?? Promise<T>.ExceptionToPromise(new Exception("The action returned null"));
81 } catch (Exception err) {
81 } catch (Exception err) {
82 return Promise<T>.ExceptionToPromise(err);
82 return Promise<T>.ExceptionToPromise(err);
83 }
83 }
84 }
84 }
85 }
85 }
86 }
86 }
General Comments 0
You need to be logged in to leave comments. Login now