Mercurial > pub > ImplabNet
comparison Implab/Promise.cs @ 6:dfa21d507bc5
*refactoring: Promise.Then now returns a new chained promise
*fixed bug with animations when multiple events fired simultaneously
| author | cin |
|---|---|
| date | Mon, 21 Oct 2013 02:34:31 +0400 |
| parents | aa367305156b |
| children | 7ea9363fef6c |
comparison
equal
deleted
inserted
replaced
| 5:f2559580b481 | 6:dfa21d507bc5 |
|---|---|
| 6 using System.Diagnostics; | 6 using System.Diagnostics; |
| 7 using System.Threading; | 7 using System.Threading; |
| 8 | 8 |
| 9 namespace Implab { | 9 namespace Implab { |
| 10 | 10 |
| 11 public delegate void ErrorHandler(Exception e); | 11 public delegate void ErrorHandler(Exception e); |
| 12 | 12 |
| 13 public delegate void ResultHandler<T>(T result); | 13 public delegate void ResultHandler<T>(T result); |
| 14 public delegate TNew ResultMapper<TSrc,TNew>(TSrc result); | 14 public delegate TNew ResultMapper<TSrc, TNew>(TSrc result); |
| 15 public delegate Promise<TNew> ChainedOperation<TSrc,TNew>(TSrc result); | 15 public delegate Promise<TNew> ChainedOperation<TSrc, TNew>(TSrc result); |
| 16 | 16 |
| 17 /// <summary> | 17 /// <summary> |
| 18 /// Класс для асинхронного получения результатов. Так называемое "обещание". | 18 /// Класс для асинхронного получения результатов. Так называемое "обещание". |
| 19 /// </summary> | 19 /// </summary> |
| 20 /// <typeparam name="T">Тип получаемого результата</typeparam> | 20 /// <typeparam name="T">Тип получаемого результата</typeparam> |
| 21 /// <remarks> | 21 /// <remarks> |
| 22 /// <para>Сервис при обращении к его методу дает обещаиние о выполнении операции, | 22 /// <para>Сервис при обращении к его методу дает обещаиние о выполнении операции, |
| 23 /// клиент получив такое обещание может установить ряд обратных вызово для получения | 23 /// клиент получив такое обещание может установить ряд обратных вызово для получения |
| 24 /// событий выполнения обещания, тоесть завершения операции и предоставлении результатов.</para> | 24 /// событий выполнения обещания, тоесть завершения операции и предоставлении результатов.</para> |
| 25 /// <para> | 25 /// <para> |
| 26 /// Обещение может быть как выполнено, так и выполнено с ошибкой. Для подписки на | 26 /// Обещение может быть как выполнено, так и выполнено с ошибкой. Для подписки на |
| 27 /// данные события клиент должен использовать методы <c>Then</c>. | 27 /// данные события клиент должен использовать методы <c>Then</c>. |
| 28 /// </para> | 28 /// </para> |
| 29 /// <para> | 29 /// <para> |
| 30 /// Сервис, в свою очередь, по окончанию выполнения операции (возможно с ошибкой), | 30 /// Сервис, в свою очередь, по окончанию выполнения операции (возможно с ошибкой), |
| 31 /// использует методы <c>Resolve</c> либо <c>Reject</c> для оповещения клиетна о | 31 /// использует методы <c>Resolve</c> либо <c>Reject</c> для оповещения клиетна о |
| 32 /// выполнении обещания. | 32 /// выполнении обещания. |
| 33 /// </para> | 33 /// </para> |
| 34 /// <para> | 34 /// <para> |
| 35 /// Если сервер успел выполнить обещание еще до того, как клиент на него подписался, | 35 /// Если сервер успел выполнить обещание еще до того, как клиент на него подписался, |
| 36 /// то в момент подписки клиента будут вызваны соответсвующие события в синхронном | 36 /// то в момент подписки клиента будут вызваны соответсвующие события в синхронном |
| 37 /// режиме и клиент будет оповещен в любом случае. Иначе, обработчики добавляются в | 37 /// режиме и клиент будет оповещен в любом случае. Иначе, обработчики добавляются в |
| 38 /// список в порядке подписания и в этом же порядке они будут вызваны при выполнении | 38 /// список в порядке подписания и в этом же порядке они будут вызваны при выполнении |
| 39 /// обещания. | 39 /// обещания. |
| 40 /// </para> | 40 /// </para> |
| 41 /// <para> | 41 /// <para> |
| 42 /// Обрабатывая результаты обещания можно преобразовывать результаты либо инициировать | 42 /// Обрабатывая результаты обещания можно преобразовывать результаты либо инициировать |
| 43 /// связанные асинхронные операции, которые также возвращают обещания. Для этого следует | 43 /// связанные асинхронные операции, которые также возвращают обещания. Для этого следует |
| 44 /// использовать соответствующую форму методе <c>Then</c>. | 44 /// использовать соответствующую форму методе <c>Then</c>. |
| 45 /// </para> | 45 /// </para> |
| 46 /// <para> | 46 /// <para> |
| 47 /// Также хорошим правилом является то, что <c>Resolve</c> и <c>Reject</c> должен вызывать | 47 /// Также хорошим правилом является то, что <c>Resolve</c> и <c>Reject</c> должен вызывать |
| 48 /// только инициатор обещания иначе могут возникнуть противоречия. | 48 /// только инициатор обещания иначе могут возникнуть противоречия. |
| 49 /// </para> | 49 /// </para> |
| 50 /// </remarks> | 50 /// </remarks> |
| 51 public class Promise<T> { | 51 public class Promise<T> { |
| 52 | 52 |
| 53 struct ResultHandlerInfo { | 53 struct ResultHandlerInfo { |
| 54 public ResultHandler<T> resultHandler; | 54 public ResultHandler<T> resultHandler; |
| 55 public ErrorHandler errorHandler; | 55 public ErrorHandler errorHandler; |
| 56 } | 56 } |
| 57 | 57 |
| 58 enum State { | 58 enum State { |
| 59 Unresolved, | 59 Unresolved, |
| 60 Resolving, | 60 Resolving, |
| 61 Resolved, | 61 Resolved, |
| 62 Cancelled | 62 Cancelled |
| 63 } | 63 } |
| 64 | 64 |
| 65 LinkedList<ResultHandlerInfo> m_handlersChain = new LinkedList<ResultHandlerInfo>(); | 65 LinkedList<ResultHandlerInfo> m_handlersChain = new LinkedList<ResultHandlerInfo>(); |
| 66 State m_state; | 66 State m_state; |
| 67 bool m_cancellable; | 67 bool m_cancellable; |
| 68 T m_result; | 68 T m_result; |
| 69 Exception m_error; | 69 Exception m_error; |
| 70 | 70 |
| 71 public Promise() { | 71 public Promise() { |
| 72 m_cancellable = true; | 72 m_cancellable = true; |
| 73 } | 73 } |
| 74 | 74 |
| 75 /// <summary> | 75 /// <summary> |
| 76 /// Событие, возникающее при отмене асинхронной операции. | 76 /// Событие, возникающее при отмене асинхронной операции. |
| 77 /// </summary> | 77 /// </summary> |
| 78 /// <description> | 78 /// <description> |
| 79 /// Как правило используется для оповещения объекта, выполняющего асинхронную операцию, о том, что ее следует отменить. | 79 /// Как правило используется для оповещения объекта, выполняющего асинхронную операцию, о том, что ее следует отменить. |
| 80 /// </description> | 80 /// </description> |
| 81 public event EventHandler Cancelled; | 81 public event EventHandler Cancelled; |
| 82 | 82 |
| 83 /// <summary> | 83 /// <summary> |
| 84 /// Выполняет обещание, сообщая об успешном выполнении. | 84 /// Выполняет обещание, сообщая об успешном выполнении. |
| 85 /// </summary> | 85 /// </summary> |
| 86 /// <param name="result">Результат выполнения.</param> | 86 /// <param name="result">Результат выполнения.</param> |
| 87 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception> | 87 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception> |
| 88 public void Resolve(T result) { | 88 public void Resolve(T result) { |
| 89 lock (this) { | 89 lock (this) { |
| 90 if (m_state == State.Cancelled) | 90 if (m_state == State.Cancelled) |
| 91 return; | 91 return; |
| 92 if (m_state != State.Unresolved) | 92 if (m_state != State.Unresolved) |
| 93 throw new InvalidOperationException("The promise is already resolved"); | 93 throw new InvalidOperationException("The promise is already resolved"); |
| 94 m_result = result; | 94 m_result = result; |
| 95 m_state = State.Resolving; | 95 m_state = State.Resolving; |
| 96 } | 96 } |
| 97 | 97 |
| 98 ResultHandlerInfo handler; | 98 ResultHandlerInfo handler; |
| 99 while (FetchNextHandler(out handler)) | 99 while (FetchNextHandler(out handler)) |
| 100 InvokeHandler(handler); | 100 InvokeHandler(handler); |
| 101 } | 101 } |
| 102 | 102 |
| 103 /// <summary> | 103 /// <summary> |
| 104 /// Выполняет обещание, сообщая об ошибке | 104 /// Выполняет обещание, сообщая об ошибке |
| 105 /// </summary> | 105 /// </summary> |
| 106 /// <param name="error">Исключение возникшее при выполнении операции</param> | 106 /// <param name="error">Исключение возникшее при выполнении операции</param> |
| 107 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception> | 107 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception> |
| 108 public void Reject(Exception error) { | 108 public void Reject(Exception error) { |
| 109 lock (this) { | 109 lock (this) { |
| 110 if (m_state == State.Cancelled) | 110 if (m_state == State.Cancelled) |
| 111 return; | 111 return; |
| 112 if (m_state != State.Unresolved) | 112 if (m_state != State.Unresolved) |
| 113 throw new InvalidOperationException("The promise is already resolved"); | 113 throw new InvalidOperationException("The promise is already resolved"); |
| 114 m_error = error; | 114 m_error = error; |
| 115 m_state = State.Resolving; | 115 m_state = State.Resolving; |
| 116 } | 116 } |
| 117 | 117 |
| 118 ResultHandlerInfo handler; | 118 ResultHandlerInfo handler; |
| 119 while (FetchNextHandler(out handler)) | 119 while (FetchNextHandler(out handler)) |
| 120 InvokeHandler(handler); | 120 InvokeHandler(handler); |
| 121 } | 121 } |
| 122 | 122 |
| 123 /// <summary> | 123 /// <summary> |
| 124 /// Отменяет операцию, если это возможно. | 124 /// Отменяет операцию, если это возможно. |
| 125 /// </summary> | 125 /// </summary> |
| 126 /// <returns><c>true</c> Операция была отменена, обработчики не будут вызваны.<c>false</c> отмена не возможна, поскольку обещание уже выполнено и обработчики отработали.</returns> | 126 /// <returns><c>true</c> Операция была отменена, обработчики не будут вызваны.<c>false</c> отмена не возможна, поскольку обещание уже выполнено и обработчики отработали.</returns> |
| 127 public bool Cancel() { | 127 public bool Cancel() { |
| 128 lock(this) { | 128 lock (this) { |
| 129 if (m_state == State.Unresolved && m_cancellable) { | 129 if (m_state == State.Unresolved && m_cancellable) { |
| 130 m_state = State.Cancelled; | 130 m_state = State.Cancelled; |
| 131 EventHandler temp = Cancelled; | 131 EventHandler temp = Cancelled; |
| 132 | 132 |
| 133 if (temp != null) | 133 if (temp != null) |
| 134 temp(this,new EventArgs()); | 134 temp(this, new EventArgs()); |
| 135 | 135 |
| 136 return true; | 136 return true; |
| 137 } else | 137 } else |
| 138 return false; | 138 return false; |
| 139 } | 139 } |
| 140 } | 140 } |
| 141 | 141 |
| 142 /// <summary> | 142 /// <summary> |
| 143 /// Добавляет обработчики событий выполнения обещания. | 143 /// Добавляет обработчики событий выполнения обещания. |
| 144 /// </summary> | 144 /// </summary> |
| 145 /// <param name="success">Обработчик успешного выполнения обещания. | 145 /// <param name="success">Обработчик успешного выполнения обещания. |
| 146 /// Данному обработчику будет передан результат выполнения операции.</param> | 146 /// Данному обработчику будет передан результат выполнения операции.</param> |
| 147 /// <param name="error">Обработчик ошибки. Данный обработчик получит | 147 /// <param name="error">Обработчик ошибки. Данный обработчик получит |
| 148 /// исключение возникшее при выполнении операции.</param> | 148 /// исключение возникшее при выполнении операции.</param> |
| 149 /// <returns>Само обещание</returns> | 149 /// <returns>Само обещание</returns> |
| 150 public Promise<T> Then(ResultHandler<T> success, ErrorHandler error) { | 150 public Promise<T> Then(ResultHandler<T> success, ErrorHandler error) { |
| 151 if (success == null && error == null) | 151 if (success == null && error == null) |
| 152 return this; | 152 return this; |
| 153 | 153 |
| 154 AddHandler(new ResultHandlerInfo() { | 154 var medium = new Promise<T>(); |
| 155 resultHandler = success, | 155 |
| 156 errorHandler = error | 156 var handlerInfo = new ResultHandlerInfo(); |
| 157 }); | 157 |
| 158 | 158 if (success != null) |
| 159 return this; | 159 handlerInfo.resultHandler = x => { |
| 160 } | 160 try { |
| 161 | 161 success(x); |
| 162 public Promise<T> Then(ResultHandler<T> success) { | 162 medium.Resolve(x); |
| 163 return Then (success, null); | 163 } catch (Exception e) { |
| 164 } | 164 medium.Reject(e); |
| 165 | 165 } |
| 166 public Promise<T> Anyway(Action handler) { | 166 }; |
| 167 if (handler == null) | 167 else |
| 168 return this; | 168 handlerInfo.resultHandler = x => medium.Resolve(x); |
| 169 AddHandler(new ResultHandlerInfo { | 169 |
| 170 resultHandler = x => handler(), | 170 if (error != null) |
| 171 errorHandler = x => handler() | 171 handlerInfo.errorHandler = x => { |
| 172 }); | 172 try { |
| 173 | 173 error(x); |
| 174 return this; | 174 } catch { } |
| 175 } | 175 medium.Reject(x); |
| 176 | 176 }; |
| 177 /// <summary> | 177 else |
| 178 /// Позволяет преобразовать результат выполения операции к новому типу. | 178 handlerInfo.errorHandler = x => medium.Reject(x); |
| 179 /// </summary> | 179 |
| 180 /// <typeparam name="TNew">Новый тип результата.</typeparam> | 180 AddHandler(handlerInfo); |
| 181 /// <param name="mapper">Преобразование результата к новому типу.</param> | 181 |
| 182 /// <param name="error">Обработчик ошибки. Данный обработчик получит | 182 return medium; |
| 183 /// исключение возникшее при выполнении операции.</param> | 183 } |
| 184 /// <returns>Новое обещание, которое будет выполнено при выполнении исходного обещания.</returns> | 184 |
| 185 public Promise<TNew> Map<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler error) { | 185 public Promise<T> Then(ResultHandler<T> success) { |
| 186 if (mapper == null) | 186 return Then(success, null); |
| 187 throw new ArgumentNullException("mapper"); | 187 } |
| 188 | 188 |
| 189 // создаем прицепленное обещание | 189 public Promise<T> Error(ErrorHandler error) { |
| 190 Promise<TNew> chained = new Promise<TNew>(); | 190 return Then(null, error); |
| 191 | 191 } |
| 192 AddHandler(new ResultHandlerInfo() { | 192 |
| 193 resultHandler = delegate(T result) { | 193 public Promise<T> Anyway(Action handler) { |
| 194 try { | 194 if (handler == null) |
| 195 // если преобразование выдаст исключение, то сработает reject сцепленного deferred | 195 return this; |
| 196 chained.Resolve(mapper(result)); | 196 |
| 197 } catch (Exception e) { | 197 var medium = new Promise<T>(); |
| 198 chained.Reject(e); | 198 |
| 199 } | 199 AddHandler(new ResultHandlerInfo { |
| 200 }, | 200 resultHandler = x => { |
| 201 errorHandler = delegate(Exception e) { | 201 try { |
| 202 if (error != null) | 202 handler(); |
| 203 error(e); | 203 medium.Resolve(x); |
| 204 // в случае ошибки нужно передать исключение дальше по цепочке | 204 } catch (Exception e) { |
| 205 chained.Reject(e); | 205 medium.Reject(e); |
| 206 } | 206 } |
| 207 }); | 207 }, |
| 208 | 208 errorHandler = x => { |
| 209 return chained; | 209 try { |
| 210 } | 210 handler(); |
| 211 | 211 } catch { } |
| 212 public Promise<TNew> Map<TNew>(ResultMapper<T, TNew> mapper) { | 212 medium.Reject(x); |
| 213 return Map (mapper, null); | 213 } |
| 214 } | 214 }); |
| 215 | 215 |
| 216 /// <summary> | 216 return medium; |
| 217 /// Сцепляет несколько аснхронных операций. Указанная асинхронная операция будет вызвана после | 217 } |
| 218 /// выполнения текущей, а результат текущей операции может быть использован для инициализации | 218 |
| 219 /// новой операции. | 219 /// <summary> |
| 220 /// </summary> | 220 /// Позволяет преобразовать результат выполения операции к новому типу. |
| 221 /// <typeparam name="TNew">Тип результата указанной асинхронной операции.</typeparam> | 221 /// </summary> |
| 222 /// <param name="chained">Асинхронная операция, которая должна будет начаться после выполнения текущей.</param> | 222 /// <typeparam name="TNew">Новый тип результата.</typeparam> |
| 223 /// <param name="error">Обработчик ошибки. Данный обработчик получит | 223 /// <param name="mapper">Преобразование результата к новому типу.</param> |
| 224 /// исключение возникшее при выполнении текуещй операции.</param> | 224 /// <param name="error">Обработчик ошибки. Данный обработчик получит |
| 225 /// <returns>Новое обещание, которое будет выполнено по окончанию указанной аснхронной операции.</returns> | 225 /// исключение возникшее при выполнении операции.</param> |
| 226 public Promise<TNew> Chain<TNew>(ChainedOperation<T, TNew> chained, ErrorHandler error) { | 226 /// <returns>Новое обещание, которое будет выполнено при выполнении исходного обещания.</returns> |
| 227 | 227 public Promise<TNew> Map<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler error) { |
| 228 // проблема в том, что на момент связывания еще не начата асинхронная операция, поэтому нужно | 228 if (mapper == null) |
| 229 // создать посредника, к которому будут подвызяваться следующие обработчики. | 229 throw new ArgumentNullException("mapper"); |
| 230 // когда будет выполнена реальная асинхронная операция, она обратиться к посреднику, чтобы | 230 |
| 231 // передать через него результаты работы. | 231 // создаем прицепленное обещание |
| 232 Promise<TNew> medium = new Promise<TNew>(); | 232 Promise<TNew> chained = new Promise<TNew>(); |
| 233 | 233 |
| 234 AddHandler(new ResultHandlerInfo() { | 234 AddHandler(new ResultHandlerInfo() { |
| 235 resultHandler = delegate(T result) { | 235 resultHandler = delegate(T result) { |
| 236 try { | 236 try { |
| 237 chained(result).Then( | 237 // если преобразование выдаст исключение, то сработает reject сцепленного deferred |
| 238 x => medium.Resolve(x), | 238 chained.Resolve(mapper(result)); |
| 239 e => medium.Reject(e) | 239 } catch (Exception e) { |
| 240 ); | 240 chained.Reject(e); |
| 241 } catch(Exception e) { | 241 } |
| 242 // если сцепленное действие выдало исключение вместо обещания, то передаем ошибку по цепочке | 242 }, |
| 243 medium.Reject(e); | 243 errorHandler = delegate(Exception e) { |
| 244 } | 244 if (error != null) |
| 245 }, | 245 error(e); |
| 246 errorHandler = delegate(Exception e) { | 246 // в случае ошибки нужно передать исключение дальше по цепочке |
| 247 if (error != null) | 247 chained.Reject(e); |
| 248 error(e); | 248 } |
| 249 // в случае ошибки нужно передать исключение дальше по цепочке | 249 }); |
| 250 medium.Reject(e); | 250 |
| 251 } | 251 return chained; |
| 252 }); | 252 } |
| 253 | 253 |
| 254 return medium; | 254 public Promise<TNew> Map<TNew>(ResultMapper<T, TNew> mapper) { |
| 255 } | 255 return Map(mapper, null); |
| 256 | 256 } |
| 257 public Promise<TNew> Chain<TNew>(ChainedOperation<T, TNew> chained) { | 257 |
| 258 return Chain (chained, null); | 258 /// <summary> |
| 259 } | 259 /// Сцепляет несколько аснхронных операций. Указанная асинхронная операция будет вызвана после |
| 260 | 260 /// выполнения текущей, а результат текущей операции может быть использован для инициализации |
| 261 /// <summary> | 261 /// новой операции. |
| 262 /// Дожидается отложенного обещания и в случае успеха, возвращает | 262 /// </summary> |
| 263 /// его, результат, в противном случае бросает исключение. | 263 /// <typeparam name="TNew">Тип результата указанной асинхронной операции.</typeparam> |
| 264 /// </summary> | 264 /// <param name="chained">Асинхронная операция, которая должна будет начаться после выполнения текущей.</param> |
| 265 /// <remarks> | 265 /// <param name="error">Обработчик ошибки. Данный обработчик получит |
| 266 /// <para> | 266 /// исключение возникшее при выполнении текуещй операции.</param> |
| 267 /// Если ожидание обещания было прервано по таймауту, это не значит, | 267 /// <returns>Новое обещание, которое будет выполнено по окончанию указанной аснхронной операции.</returns> |
| 268 /// что обещание было отменено или что-то в этом роде, это только | 268 public Promise<TNew> Chain<TNew>(ChainedOperation<T, TNew> chained, ErrorHandler error) { |
| 269 /// означает, что мы его не дождались, однако все зарегистрированные | 269 |
| 270 /// обработчики, как были так остались и они будут вызваны, когда | 270 // проблема в том, что на момент связывания еще не начата асинхронная операция, поэтому нужно |
| 271 /// обещание будет выполнено. | 271 // создать посредника, к которому будут подвызяваться следующие обработчики. |
| 272 /// </para> | 272 // когда будет выполнена реальная асинхронная операция, она обратиться к посреднику, чтобы |
| 273 /// <para> | 273 // передать через него результаты работы. |
| 274 /// Такое поведение вполне оправдано поскольку таймаут может истечь | 274 Promise<TNew> medium = new Promise<TNew>(); |
| 275 /// в тот момент, когда началась обработка цепочки обработчиков, и | 275 |
| 276 /// к тому же текущее обещание может стоять в цепочке обещаний и его | 276 AddHandler(new ResultHandlerInfo() { |
| 277 /// отклонение может привести к непрогнозируемому результату. | 277 resultHandler = delegate(T result) { |
| 278 /// </para> | 278 try { |
| 279 /// </remarks> | 279 chained(result).Then( |
| 280 /// <param name="timeout">Время ожидания</param> | 280 x => medium.Resolve(x), |
| 281 /// <returns>Результат выполнения обещания</returns> | 281 e => medium.Reject(e) |
| 282 public T Join(int timeout) { | 282 ); |
| 283 ManualResetEvent evt = new ManualResetEvent(false); | 283 } catch (Exception e) { |
| 284 Anyway(() => evt.Set()); | 284 // если сцепленное действие выдало исключение вместо обещания, то передаем ошибку по цепочке |
| 285 | 285 medium.Reject(e); |
| 286 if (!evt.WaitOne(timeout, true)) | 286 } |
| 287 throw new TimeoutException(); | 287 }, |
| 288 | 288 errorHandler = delegate(Exception e) { |
| 289 if (m_error != null) | 289 if (error != null) |
| 290 throw new TargetInvocationException( m_error ); | 290 error(e); |
| 291 else | 291 // в случае ошибки нужно передать исключение дальше по цепочке |
| 292 return m_result; | 292 medium.Reject(e); |
| 293 } | 293 } |
| 294 | 294 }); |
| 295 public T Join() { | 295 |
| 296 return Join(Timeout.Infinite); | 296 return medium; |
| 297 } | 297 } |
| 298 | 298 |
| 299 /// <summary> | 299 public Promise<TNew> Chain<TNew>(ChainedOperation<T, TNew> chained) { |
| 300 /// Данный метод последовательно извлекает обработчики обещания и когда | 300 return Chain(chained, null); |
| 301 /// их больше не осталось - ставит состояние "разрешено". | 301 } |
| 302 /// </summary> | 302 |
| 303 /// <param name="handler">Информация об обработчике</param> | 303 /// <summary> |
| 304 /// <returns>Признак того, что еще остались обработчики в очереди</returns> | 304 /// Дожидается отложенного обещания и в случае успеха, возвращает |
| 305 bool FetchNextHandler(out ResultHandlerInfo handler) { | 305 /// его, результат, в противном случае бросает исключение. |
| 306 handler = default(ResultHandlerInfo); | 306 /// </summary> |
| 307 | 307 /// <remarks> |
| 308 lock (this) { | 308 /// <para> |
| 309 Debug.Assert(m_state == State.Resolving); | 309 /// Если ожидание обещания было прервано по таймауту, это не значит, |
| 310 | 310 /// что обещание было отменено или что-то в этом роде, это только |
| 311 if (m_handlersChain.Count > 0) { | 311 /// означает, что мы его не дождались, однако все зарегистрированные |
| 312 handler = m_handlersChain.First.Value; | 312 /// обработчики, как были так остались и они будут вызваны, когда |
| 313 m_handlersChain.RemoveFirst(); | 313 /// обещание будет выполнено. |
| 314 return true; | 314 /// </para> |
| 315 } else { | 315 /// <para> |
| 316 m_state = State.Resolved; | 316 /// Такое поведение вполне оправдано поскольку таймаут может истечь |
| 317 return false; | 317 /// в тот момент, когда началась обработка цепочки обработчиков, и |
| 318 } | 318 /// к тому же текущее обещание может стоять в цепочке обещаний и его |
| 319 } | 319 /// отклонение может привести к непрогнозируемому результату. |
| 320 } | 320 /// </para> |
| 321 | 321 /// </remarks> |
| 322 void AddHandler(ResultHandlerInfo handler) { | 322 /// <param name="timeout">Время ожидания</param> |
| 323 bool invokeRequired = false; | 323 /// <returns>Результат выполнения обещания</returns> |
| 324 | 324 public T Join(int timeout) { |
| 325 lock (this) { | 325 ManualResetEvent evt = new ManualResetEvent(false); |
| 326 if (m_state != State.Resolved) | 326 Anyway(() => evt.Set()); |
| 327 m_handlersChain.AddLast(handler); | 327 |
| 328 else | 328 if (!evt.WaitOne(timeout, true)) |
| 329 invokeRequired = true; | 329 throw new TimeoutException(); |
| 330 } | 330 |
| 331 | 331 if (m_error != null) |
| 332 // обработчики не должны блокировать сам объект | 332 throw new TargetInvocationException(m_error); |
| 333 if (invokeRequired) | 333 else |
| 334 InvokeHandler(handler); | 334 return m_result; |
| 335 } | 335 } |
| 336 | 336 |
| 337 void InvokeHandler(ResultHandlerInfo handler) { | 337 public T Join() { |
| 338 if (m_error == null) { | 338 return Join(Timeout.Infinite); |
| 339 try { | 339 } |
| 340 if (handler.resultHandler != null) | 340 |
| 341 handler.resultHandler(m_result); | 341 /// <summary> |
| 342 } catch { } | 342 /// Данный метод последовательно извлекает обработчики обещания и когда |
| 343 } | 343 /// их больше не осталось - ставит состояние "разрешено". |
| 344 | 344 /// </summary> |
| 345 if (m_error != null) { | 345 /// <param name="handler">Информация об обработчике</param> |
| 346 try { | 346 /// <returns>Признак того, что еще остались обработчики в очереди</returns> |
| 347 if (handler.errorHandler !=null) | 347 bool FetchNextHandler(out ResultHandlerInfo handler) { |
| 348 handler.errorHandler(m_error); | 348 handler = default(ResultHandlerInfo); |
| 349 } catch { } | 349 |
| 350 } | 350 lock (this) { |
| 351 } | 351 Debug.Assert(m_state == State.Resolving); |
| 352 | 352 |
| 353 | 353 if (m_handlersChain.Count > 0) { |
| 354 } | 354 handler = m_handlersChain.First.Value; |
| 355 m_handlersChain.RemoveFirst(); | |
| 356 return true; | |
| 357 } else { | |
| 358 m_state = State.Resolved; | |
| 359 return false; | |
| 360 } | |
| 361 } | |
| 362 } | |
| 363 | |
| 364 void AddHandler(ResultHandlerInfo handler) { | |
| 365 bool invokeRequired = false; | |
| 366 | |
| 367 lock (this) { | |
| 368 if (m_state != State.Resolved) | |
| 369 m_handlersChain.AddLast(handler); | |
| 370 else | |
| 371 invokeRequired = true; | |
| 372 } | |
| 373 | |
| 374 // обработчики не должны блокировать сам объект | |
| 375 if (invokeRequired) | |
| 376 InvokeHandler(handler); | |
| 377 } | |
| 378 | |
| 379 void InvokeHandler(ResultHandlerInfo handler) { | |
| 380 if (m_error == null) { | |
| 381 try { | |
| 382 if (handler.resultHandler != null) | |
| 383 handler.resultHandler(m_result); | |
| 384 } catch { } | |
| 385 } | |
| 386 | |
| 387 if (m_error != null) { | |
| 388 try { | |
| 389 if (handler.errorHandler != null) | |
| 390 handler.errorHandler(m_error); | |
| 391 } catch { } | |
| 392 } | |
| 393 } | |
| 394 | |
| 395 | |
| 396 } | |
| 355 } | 397 } |
