##// END OF EJS Templates
inital progress handling
cin -
r7:7ea9363fef6c promises
parent child
Show More
@@ -0,0 +1,33
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 namespace Implab
7 {
8 public interface IPromise
9 {
10 /// <summary>
11 /// Check whereather the promise has no more than one dependent promise.
12 /// </summary>
13 bool IsExclusive
14 {
15 get;
16 }
17
18 /// <summary>
19 /// The current state of the promise.
20 /// </summary>
21 PromiseState State
22 {
23 get;
24 }
25
26 /// <summary>
27 /// Tries to cancel the promise or the complete chain.
28 /// </summary>
29 /// <param name="dependencies">Try to cancel the parent promise is it has the only one child</param>
30 /// <returns></returns>
31 bool Cancel(bool dependencies);
32 }
33 }
@@ -0,0 +1,36
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 namespace Implab
7 {
8
9 public class ProgressInitEventArgs: EventArgs
10 {
11 public float MaxProgress
12 {
13 get;
14 private set;
15 }
16
17 public float CurrentProgress
18 {
19 get;
20 private set;
21 }
22
23 public string Message
24 {
25 get;
26 private set;
27 }
28
29 public ProgressInitEventArgs(float current, float max, string message)
30 {
31 this.MaxProgress = max;
32 this.CurrentProgress = current;
33 this.Message = message;
34 }
35 }
36 }
@@ -0,0 +1,15
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 namespace Implab
7 {
8 public enum PromiseState
9 {
10 Unresolved,
11 Resolved,
12 Cancelled,
13 Rejected
14 }
15 }
@@ -0,0 +1,113
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading;
6
7 namespace Implab
8 {
9 /// <summary>
10 /// This class allows to interact with asyncronuos task.
11 /// </summary>
12 /// <remarks>
13 /// Members of this object are thread safe.
14 /// </remarks>
15 class TaskController
16 {
17 object m_lock;
18 string m_message;
19 bool m_cancelled;
20
21 float m_current;
22 float m_max;
23
24 public event EventHandler<ValueEventArgs<string>> MessageUpdated;
25 public event EventHandler<ValueEventArgs<float>> ProgressUpdated;
26 public event EventHandler<ProgressInitEventArgs> ProgressInit;
27
28 public TaskController()
29 {
30 m_lock = new Object();
31 }
32
33 public string Message
34 {
35 get
36 {
37 lock (m_lock)
38 return m_message;
39 }
40 set
41 {
42 lock (m_lock)
43 {
44 m_message = value;
45 OnMessageUpdated();
46 }
47 }
48 }
49
50 public float CurrentProgress
51 {
52 get
53 {
54 lock (m_lock)
55 return m_current;
56 }
57 set
58 {
59 lock (m_lock)
60 {
61 var prev = m_current;
62 m_current = value;
63 if (m_current >= m_max)
64 m_current = m_max;
65 if (m_current != prev)
66 OnProgressUpdated();
67 }
68 }
69 }
70
71 public void InitProgress(float current, float max, string message)
72 {
73 if (max < 0)
74 throw new ArgumentOutOfRangeException("max");
75 if (current < 0 || current > max)
76 throw new ArgumentOutOfRangeException("current");
77
78 lock(m_lock) {
79 m_current = current;
80 m_max = max;
81 m_message = message;
82 OnProgressInit();
83 }
84 }
85
86 protected virtual void OnMessageUpdated()
87 {
88 var temp = MessageUpdated;
89 if (temp != null)
90 {
91 temp(this, new ValueEventArgs<string>(m_message));
92 }
93 }
94
95 protected virtual void OnProgressUpdated()
96 {
97 var temp = ProgressUpdated;
98 if (temp != null)
99 {
100 temp(this,new ValueEventArgs<float>(m_current));
101 }
102 }
103
104 protected virtual void OnProgressInit()
105 {
106 var temp = ProgressInit;
107 if (temp != null)
108 {
109 temp(this, new ProgressInitEventArgs(m_current,m_max, m_message));
110 }
111 }
112 }
113 }
@@ -0,0 +1,20
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5
6 namespace Implab
7 {
8 public class ValueEventArgs<T>: EventArgs
9 {
10 public ValueEventArgs(T value)
11 {
12 this.Value = value;
13 }
14 public T Value
15 {
16 get;
17 private set;
18 }
19 }
20 }
1 NO CONTENT: modified file, binary diff hidden
NO CONTENT: modified file, binary diff hidden
@@ -1,44 +1,48
1 <?xml version="1.0" encoding="utf-8"?>
1 <?xml version="1.0" encoding="utf-8"?>
2 <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
2 <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3 <PropertyGroup>
3 <PropertyGroup>
4 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
4 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
5 <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
5 <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
6 <ProductVersion>10.0.0</ProductVersion>
6 <ProductVersion>10.0.0</ProductVersion>
7 <SchemaVersion>2.0</SchemaVersion>
7 <SchemaVersion>2.0</SchemaVersion>
8 <ProjectGuid>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</ProjectGuid>
8 <ProjectGuid>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</ProjectGuid>
9 <OutputType>Library</OutputType>
9 <OutputType>Library</OutputType>
10 <RootNamespace>Implab</RootNamespace>
10 <RootNamespace>Implab</RootNamespace>
11 <AssemblyName>Implab</AssemblyName>
11 <AssemblyName>Implab</AssemblyName>
12 </PropertyGroup>
12 </PropertyGroup>
13 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
13 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
14 <DebugSymbols>true</DebugSymbols>
14 <DebugSymbols>true</DebugSymbols>
15 <DebugType>full</DebugType>
15 <DebugType>full</DebugType>
16 <Optimize>false</Optimize>
16 <Optimize>false</Optimize>
17 <OutputPath>bin\Debug</OutputPath>
17 <OutputPath>bin\Debug</OutputPath>
18 <DefineConstants>DEBUG;</DefineConstants>
18 <DefineConstants>DEBUG;</DefineConstants>
19 <ErrorReport>prompt</ErrorReport>
19 <ErrorReport>prompt</ErrorReport>
20 <WarningLevel>4</WarningLevel>
20 <WarningLevel>4</WarningLevel>
21 <ConsolePause>false</ConsolePause>
21 <ConsolePause>false</ConsolePause>
22 </PropertyGroup>
22 </PropertyGroup>
23 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
23 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
24 <DebugType>full</DebugType>
24 <DebugType>full</DebugType>
25 <Optimize>true</Optimize>
25 <Optimize>true</Optimize>
26 <OutputPath>bin\Release</OutputPath>
26 <OutputPath>bin\Release</OutputPath>
27 <ErrorReport>prompt</ErrorReport>
27 <ErrorReport>prompt</ErrorReport>
28 <WarningLevel>4</WarningLevel>
28 <WarningLevel>4</WarningLevel>
29 <ConsolePause>false</ConsolePause>
29 <ConsolePause>false</ConsolePause>
30 </PropertyGroup>
30 </PropertyGroup>
31 <ItemGroup>
31 <ItemGroup>
32 <Reference Include="System" />
32 <Reference Include="System" />
33 </ItemGroup>
33 </ItemGroup>
34 <ItemGroup>
34 <ItemGroup>
35 <Compile Include="ICancellable.cs" />
36 <Compile Include="TaskController.cs" />
37 <Compile Include="ProgressInitEventArgs.cs" />
35 <Compile Include="Properties\AssemblyInfo.cs" />
38 <Compile Include="Properties\AssemblyInfo.cs" />
36 <Compile Include="Promise.cs" />
39 <Compile Include="Promise.cs" />
37 <Compile Include="AsyncPool.cs" />
40 <Compile Include="AsyncPool.cs" />
38 <Compile Include="Safe.cs" />
41 <Compile Include="Safe.cs" />
42 <Compile Include="ValueEventArgs.cs" />
39 </ItemGroup>
43 </ItemGroup>
40 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
44 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
41 <ItemGroup>
45 <ItemGroup>
42 <Folder Include="Parallels\" />
46 <Folder Include="Parallels\" />
43 </ItemGroup>
47 </ItemGroup>
44 </Project> No newline at end of file
48 </Project>
@@ -1,397 +1,399
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.Reflection;
4 using System.Reflection;
5 using System.Text;
5 using System.Text;
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>: IPromise {
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 public Action cancelHandler;
57
58 enum State {
59 Unresolved,
60 Resolving,
61 Resolved,
62 Cancelled
63 }
57 }
64
58
65 LinkedList<ResultHandlerInfo> m_handlersChain = new LinkedList<ResultHandlerInfo>();
59 LinkedList<ResultHandlerInfo> m_handlersChain = new LinkedList<ResultHandlerInfo>();
66 State m_state;
60 PromiseState m_state;
67 bool m_cancellable;
61 bool m_cancellable;
68 T m_result;
62 T m_result;
69 Exception m_error;
63 Exception m_error;
64 IPromise m_parent;
65 int m_childrenCount;
70
66
71 public Promise() {
67 public Promise() {
72 m_cancellable = true;
68 m_cancellable = true;
73 }
69 }
74
70
71 public Promise(IPromise parent, bool cancellable)
72 {
73 m_cancellable = cancellable;
74 m_parent = parent;
75 }
76
75 /// <summary>
77 /// <summary>
76 /// Событие, возникающее при отмене асинхронной операции.
78 /// Событие, возникающее при отмене асинхронной операции.
77 /// </summary>
79 /// </summary>
78 /// <description>
80 /// <description>
79 /// Как правило используется для оповещения объекта, выполняющего асинхронную операцию, о том, что ее следует отменить.
81 /// Как правило используется для оповещения объекта, выполняющего асинхронную операцию, о том, что ее следует отменить.
80 /// </description>
82 /// </description>
81 public event EventHandler Cancelled;
83 public event EventHandler Cancelled;
82
84
83 /// <summary>
85 /// <summary>
84 /// Выполняет обещание, сообщая об успешном выполнении.
86 /// Выполняет обещание, сообщая об успешном выполнении.
85 /// </summary>
87 /// </summary>
86 /// <param name="result">Результат выполнения.</param>
88 /// <param name="result">Результат выполнения.</param>
87 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception>
89 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception>
88 public void Resolve(T result) {
90 public void Resolve(T result) {
89 lock (this) {
91 lock (this) {
90 if (m_state == State.Cancelled)
92 if (m_state == PromiseState.Cancelled)
91 return;
93 return;
92 if (m_state != State.Unresolved)
94 if (m_state != PromiseState.Unresolved)
93 throw new InvalidOperationException("The promise is already resolved");
95 throw new InvalidOperationException("The promise is already resolved");
94 m_result = result;
96 m_result = result;
95 m_state = State.Resolving;
97 m_state = PromiseState.Resolved;
96 }
98 }
97
99
98 ResultHandlerInfo handler;
100 ResultHandlerInfo handler;
99 while (FetchNextHandler(out handler))
101 while (FetchNextHandler(out handler))
100 InvokeHandler(handler);
102 InvokeHandler(handler);
101 }
103 }
102
104
103 /// <summary>
105 /// <summary>
104 /// Выполняет обещание, сообщая об ошибке
106 /// Выполняет обещание, сообщая об ошибке
105 /// </summary>
107 /// </summary>
106 /// <param name="error">Исключение возникшее при выполнении операции</param>
108 /// <param name="error">Исключение возникшее при выполнении операции</param>
107 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception>
109 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception>
108 public void Reject(Exception error) {
110 public void Reject(Exception error) {
109 lock (this) {
111 lock (this) {
110 if (m_state == State.Cancelled)
112 if (m_state == PromiseState.Cancelled)
111 return;
113 return;
112 if (m_state != State.Unresolved)
114 if (m_state != PromiseState.Unresolved)
113 throw new InvalidOperationException("The promise is already resolved");
115 throw new InvalidOperationException("The promise is already resolved");
114 m_error = error;
116 m_error = error;
115 m_state = State.Resolving;
117 m_state = PromiseState.Rejected;
116 }
118 }
117
119
118 ResultHandlerInfo handler;
120 ResultHandlerInfo handler;
119 while (FetchNextHandler(out handler))
121 while (FetchNextHandler(out handler))
120 InvokeHandler(handler);
122 InvokeHandler(handler);
121 }
123 }
122
124
123 /// <summary>
125 /// <summary>
124 /// Отменяет операцию, если это возможно.
126 /// Отменяет операцию, если это возможно.
125 /// </summary>
127 /// </summary>
126 /// <returns><c>true</c> Операция была отменена, обработчики не будут вызваны.<c>false</c> отмена не возможна, поскольку обещание уже выполнено и обработчики отработали.</returns>
128 /// <returns><c>true</c> Операция была отменена, обработчики не будут вызваны.<c>false</c> отмена не возможна, поскольку обещание уже выполнено и обработчики отработали.</returns>
127 public bool Cancel() {
129 public bool Cancel() {
128 lock (this) {
130 lock (this) {
129 if (m_state == State.Unresolved && m_cancellable) {
131 if (m_state == PromiseState.Unresolved && m_cancellable)
130 m_state = State.Cancelled;
132 {
133 m_state = PromiseState.Cancelled;
131 EventHandler temp = Cancelled;
134 EventHandler temp = Cancelled;
132
135
133 if (temp != null)
136 if (temp != null)
134 temp(this, new EventArgs());
137 temp(this, new EventArgs());
135
138
136 return true;
139 return true;
137 } else
140 } else
138 return false;
141 return false;
139 }
142 }
140 }
143 }
141
144
142 /// <summary>
145 /// <summary>
143 /// Добавляет обработчики событий выполнения обещания.
146 /// Добавляет обработчики событий выполнения обещания.
144 /// </summary>
147 /// </summary>
145 /// <param name="success">Обработчик успешного выполнения обещания.
148 /// <param name="success">Обработчик успешного выполнения обещания.
146 /// Данному обработчику будет передан результат выполнения операции.</param>
149 /// Данному обработчику будет передан результат выполнения операции.</param>
147 /// <param name="error">Обработчик ошибки. Данный обработчик получит
150 /// <param name="error">Обработчик ошибки. Данный обработчик получит
148 /// исключение возникшее при выполнении операции.</param>
151 /// исключение возникшее при выполнении операции.</param>
149 /// <returns>Само обещание</returns>
152 /// <returns>Само обещание</returns>
150 public Promise<T> Then(ResultHandler<T> success, ErrorHandler error) {
153 public Promise<T> Then(ResultHandler<T> success, ErrorHandler error) {
151 if (success == null && error == null)
154 if (success == null && error == null)
152 return this;
155 return this;
153
156
154 var medium = new Promise<T>();
157 var medium = new Promise<T>();
155
158
156 var handlerInfo = new ResultHandlerInfo();
159 var handlerInfo = new ResultHandlerInfo();
157
160
158 if (success != null)
161 if (success != null)
159 handlerInfo.resultHandler = x => {
162 handlerInfo.resultHandler = x => {
160 try {
163 try {
161 success(x);
164 success(x);
162 medium.Resolve(x);
165 medium.Resolve(x);
163 } catch (Exception e) {
166 } catch (Exception e) {
164 medium.Reject(e);
167 medium.Reject(e);
165 }
168 }
166 };
169 };
167 else
170 else
168 handlerInfo.resultHandler = x => medium.Resolve(x);
171 handlerInfo.resultHandler = x => medium.Resolve(x);
169
172
170 if (error != null)
173 if (error != null)
171 handlerInfo.errorHandler = x => {
174 handlerInfo.errorHandler = x => {
172 try {
175 try {
173 error(x);
176 error(x);
174 } catch { }
177 } catch { }
175 medium.Reject(x);
178 medium.Reject(x);
176 };
179 };
177 else
180 else
178 handlerInfo.errorHandler = x => medium.Reject(x);
181 handlerInfo.errorHandler = x => medium.Reject(x);
179
182
180 AddHandler(handlerInfo);
183 AddHandler(handlerInfo);
181
184
182 return medium;
185 return medium;
183 }
186 }
184
187
185 public Promise<T> Then(ResultHandler<T> success) {
188 public Promise<T> Then(ResultHandler<T> success) {
186 return Then(success, null);
189 return Then(success, null);
187 }
190 }
188
191
189 public Promise<T> Error(ErrorHandler error) {
192 public Promise<T> Error(ErrorHandler error) {
190 return Then(null, error);
193 return Then(null, error);
191 }
194 }
192
195
193 public Promise<T> Anyway(Action handler) {
196 public Promise<T> Anyway(Action handler) {
194 if (handler == null)
197 if (handler == null)
195 return this;
198 return this;
196
199
197 var medium = new Promise<T>();
200 var medium = new Promise<T>();
198
201
199 AddHandler(new ResultHandlerInfo {
202 AddHandler(new ResultHandlerInfo {
200 resultHandler = x => {
203 resultHandler = x => {
201 try {
204 try {
202 handler();
205 handler();
203 medium.Resolve(x);
206 medium.Resolve(x);
204 } catch (Exception e) {
207 } catch (Exception e) {
205 medium.Reject(e);
208 medium.Reject(e);
206 }
209 }
207 },
210 },
208 errorHandler = x => {
211 errorHandler = x => {
209 try {
212 try {
210 handler();
213 handler();
211 } catch { }
214 } catch { }
212 medium.Reject(x);
215 medium.Reject(x);
213 }
216 }
214 });
217 });
215
218
216 return medium;
219 return medium;
217 }
220 }
218
221
219 /// <summary>
222 /// <summary>
220 /// Позволяет преобразовать результат выполения операции к новому типу.
223 /// Позволяет преобразовать результат выполения операции к новому типу.
221 /// </summary>
224 /// </summary>
222 /// <typeparam name="TNew">Новый тип результата.</typeparam>
225 /// <typeparam name="TNew">Новый тип результата.</typeparam>
223 /// <param name="mapper">Преобразование результата к новому типу.</param>
226 /// <param name="mapper">Преобразование результата к новому типу.</param>
224 /// <param name="error">Обработчик ошибки. Данный обработчик получит
227 /// <param name="error">Обработчик ошибки. Данный обработчик получит
225 /// исключение возникшее при выполнении операции.</param>
228 /// исключение возникшее при выполнении операции.</param>
226 /// <returns>Новое обещание, которое будет выполнено при выполнении исходного обещания.</returns>
229 /// <returns>Новое обещание, которое будет выполнено при выполнении исходного обещания.</returns>
227 public Promise<TNew> Map<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler error) {
230 public Promise<TNew> Map<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler error) {
228 if (mapper == null)
231 if (mapper == null)
229 throw new ArgumentNullException("mapper");
232 throw new ArgumentNullException("mapper");
230
233
231 // создаем прицепленное обещание
234 // создаем прицепленное обещание
232 Promise<TNew> chained = new Promise<TNew>();
235 Promise<TNew> chained = new Promise<TNew>();
233
236
234 AddHandler(new ResultHandlerInfo() {
237 AddHandler(new ResultHandlerInfo() {
235 resultHandler = delegate(T result) {
238 resultHandler = delegate(T result) {
236 try {
239 try {
237 // если преобразование выдаст исключение, то сработает reject сцепленного deferred
240 // если преобразование выдаст исключение, то сработает reject сцепленного deferred
238 chained.Resolve(mapper(result));
241 chained.Resolve(mapper(result));
239 } catch (Exception e) {
242 } catch (Exception e) {
240 chained.Reject(e);
243 chained.Reject(e);
241 }
244 }
242 },
245 },
243 errorHandler = delegate(Exception e) {
246 errorHandler = delegate(Exception e) {
244 if (error != null)
247 if (error != null)
245 error(e);
248 error(e);
246 // в случае ошибки нужно передать исключение дальше по цепочке
249 // в случае ошибки нужно передать исключение дальше по цепочке
247 chained.Reject(e);
250 chained.Reject(e);
248 }
251 }
249 });
252 });
250
253
251 return chained;
254 return chained;
252 }
255 }
253
256
254 public Promise<TNew> Map<TNew>(ResultMapper<T, TNew> mapper) {
257 public Promise<TNew> Map<TNew>(ResultMapper<T, TNew> mapper) {
255 return Map(mapper, null);
258 return Map(mapper, null);
256 }
259 }
257
260
258 /// <summary>
261 /// <summary>
259 /// Сцепляет несколько аснхронных операций. Указанная асинхронная операция будет вызвана после
262 /// Сцепляет несколько аснхронных операций. Указанная асинхронная операция будет вызвана после
260 /// выполнения текущей, а результат текущей операции может быть использован для инициализации
263 /// выполнения текущей, а результат текущей операции может быть использован для инициализации
261 /// новой операции.
264 /// новой операции.
262 /// </summary>
265 /// </summary>
263 /// <typeparam name="TNew">Тип результата указанной асинхронной операции.</typeparam>
266 /// <typeparam name="TNew">Тип результата указанной асинхронной операции.</typeparam>
264 /// <param name="chained">Асинхронная операция, которая должна будет начаться после выполнения текущей.</param>
267 /// <param name="chained">Асинхронная операция, которая должна будет начаться после выполнения текущей.</param>
265 /// <param name="error">Обработчик ошибки. Данный обработчик получит
268 /// <param name="error">Обработчик ошибки. Данный обработчик получит
266 /// исключение возникшее при выполнении текуещй операции.</param>
269 /// исключение возникшее при выполнении текуещй операции.</param>
267 /// <returns>Новое обещание, которое будет выполнено по окончанию указанной аснхронной операции.</returns>
270 /// <returns>Новое обещание, которое будет выполнено по окончанию указанной аснхронной операции.</returns>
268 public Promise<TNew> Chain<TNew>(ChainedOperation<T, TNew> chained, ErrorHandler error) {
271 public Promise<TNew> Chain<TNew>(ChainedOperation<T, TNew> chained, ErrorHandler error) {
269
272
270 // проблема в том, что на момент связывания еще не начата асинхронная операция, поэтому нужно
273 // проблема в том, что на момент связывания еще не начата асинхронная операция, поэтому нужно
271 // создать посредника, к которому будут подвызяваться следующие обработчики.
274 // создать посредника, к которому будут подвызяваться следующие обработчики.
272 // когда будет выполнена реальная асинхронная операция, она обратиться к посреднику, чтобы
275 // когда будет выполнена реальная асинхронная операция, она обратиться к посреднику, чтобы
273 // передать через него результаты работы.
276 // передать через него результаты работы.
274 Promise<TNew> medium = new Promise<TNew>();
277 Promise<TNew> medium = new Promise<TNew>();
275
278
276 AddHandler(new ResultHandlerInfo() {
279 AddHandler(new ResultHandlerInfo() {
277 resultHandler = delegate(T result) {
280 resultHandler = delegate(T result) {
278 try {
281 try {
279 chained(result).Then(
282 chained(result).Then(
280 x => medium.Resolve(x),
283 x => medium.Resolve(x),
281 e => medium.Reject(e)
284 e => medium.Reject(e)
282 );
285 );
283 } catch (Exception e) {
286 } catch (Exception e) {
284 // если сцепленное действие выдало исключение вместо обещания, то передаем ошибку по цепочке
287 // если сцепленное действие выдало исключение вместо обещания, то передаем ошибку по цепочке
285 medium.Reject(e);
288 medium.Reject(e);
286 }
289 }
287 },
290 },
288 errorHandler = delegate(Exception e) {
291 errorHandler = delegate(Exception e) {
289 if (error != null)
292 if (error != null)
290 error(e);
293 error(e);
291 // в случае ошибки нужно передать исключение дальше по цепочке
294 // в случае ошибки нужно передать исключение дальше по цепочке
292 medium.Reject(e);
295 medium.Reject(e);
293 }
296 }
294 });
297 });
295
298
296 return medium;
299 return medium;
297 }
300 }
298
301
299 public Promise<TNew> Chain<TNew>(ChainedOperation<T, TNew> chained) {
302 public Promise<TNew> Chain<TNew>(ChainedOperation<T, TNew> chained) {
300 return Chain(chained, null);
303 return Chain(chained, null);
301 }
304 }
302
305
303 /// <summary>
306 /// <summary>
304 /// Дожидается отложенного обещания и в случае успеха, возвращает
307 /// Дожидается отложенного обещания и в случае успеха, возвращает
305 /// его, результат, в противном случае бросает исключение.
308 /// его, результат, в противном случае бросает исключение.
306 /// </summary>
309 /// </summary>
307 /// <remarks>
310 /// <remarks>
308 /// <para>
311 /// <para>
309 /// Если ожидание обещания было прервано по таймауту, это не значит,
312 /// Если ожидание обещания было прервано по таймауту, это не значит,
310 /// что обещание было отменено или что-то в этом роде, это только
313 /// что обещание было отменено или что-то в этом роде, это только
311 /// означает, что мы его не дождались, однако все зарегистрированные
314 /// означает, что мы его не дождались, однако все зарегистрированные
312 /// обработчики, как были так остались и они будут вызваны, когда
315 /// обработчики, как были так остались и они будут вызваны, когда
313 /// обещание будет выполнено.
316 /// обещание будет выполнено.
314 /// </para>
317 /// </para>
315 /// <para>
318 /// <para>
316 /// Такое поведение вполне оправдано поскольку таймаут может истечь
319 /// Такое поведение вполне оправдано поскольку таймаут может истечь
317 /// в тот момент, когда началась обработка цепочки обработчиков, и
320 /// в тот момент, когда началась обработка цепочки обработчиков, и
318 /// к тому же текущее обещание может стоять в цепочке обещаний и его
321 /// к тому же текущее обещание может стоять в цепочке обещаний и его
319 /// отклонение может привести к непрогнозируемому результату.
322 /// отклонение может привести к непрогнозируемому результату.
320 /// </para>
323 /// </para>
321 /// </remarks>
324 /// </remarks>
322 /// <param name="timeout">Время ожидания</param>
325 /// <param name="timeout">Время ожидания</param>
323 /// <returns>Результат выполнения обещания</returns>
326 /// <returns>Результат выполнения обещания</returns>
324 public T Join(int timeout) {
327 public T Join(int timeout) {
325 ManualResetEvent evt = new ManualResetEvent(false);
328 ManualResetEvent evt = new ManualResetEvent(false);
326 Anyway(() => evt.Set());
329 Anyway(() => evt.Set());
327
330
328 if (!evt.WaitOne(timeout, true))
331 if (!evt.WaitOne(timeout, true))
329 throw new TimeoutException();
332 throw new TimeoutException();
330
333
331 if (m_error != null)
334 if (m_error != null)
332 throw new TargetInvocationException(m_error);
335 throw new TargetInvocationException(m_error);
333 else
336 else
334 return m_result;
337 return m_result;
335 }
338 }
336
339
337 public T Join() {
340 public T Join() {
338 return Join(Timeout.Infinite);
341 return Join(Timeout.Infinite);
339 }
342 }
340
343
341 /// <summary>
344 /// <summary>
342 /// Данный метод последовательно извлекает обработчики обещания и когда
345 /// Данный метод последовательно извлекает обработчики обещания и когда
343 /// их больше не осталось - ставит состояние "разрешено".
346 /// их больше не осталось - ставит состояние "разрешено".
344 /// </summary>
347 /// </summary>
345 /// <param name="handler">Информация об обработчике</param>
348 /// <param name="handler">Информация об обработчике</param>
346 /// <returns>Признак того, что еще остались обработчики в очереди</returns>
349 /// <returns>Признак того, что еще остались обработчики в очереди</returns>
347 bool FetchNextHandler(out ResultHandlerInfo handler) {
350 bool FetchNextHandler(out ResultHandlerInfo handler) {
348 handler = default(ResultHandlerInfo);
351 handler = default(ResultHandlerInfo);
349
352
350 lock (this) {
353 lock (this) {
351 Debug.Assert(m_state == State.Resolving);
354 Debug.Assert(m_state != PromiseState.Unresolved);
352
355
353 if (m_handlersChain.Count > 0) {
356 if (m_handlersChain.Count > 0) {
354 handler = m_handlersChain.First.Value;
357 handler = m_handlersChain.First.Value;
355 m_handlersChain.RemoveFirst();
358 m_handlersChain.RemoveFirst();
356 return true;
359 return true;
357 } else {
360 } else {
358 m_state = State.Resolved;
359 return false;
361 return false;
360 }
362 }
361 }
363 }
362 }
364 }
363
365
364 void AddHandler(ResultHandlerInfo handler) {
366 void AddHandler(ResultHandlerInfo handler) {
365 bool invokeRequired = false;
367 bool invokeRequired = false;
366
368
367 lock (this) {
369 lock (this) {
368 if (m_state != State.Resolved)
370 if (m_state == PromiseState.Unresolved)
369 m_handlersChain.AddLast(handler);
371 m_handlersChain.AddLast(handler);
370 else
372 else
371 invokeRequired = true;
373 invokeRequired = true;
372 }
374 }
373
375
374 // обработчики не должны блокировать сам объект
376 // обработчики не должны блокировать сам объект
375 if (invokeRequired)
377 if (invokeRequired)
376 InvokeHandler(handler);
378 InvokeHandler(handler);
377 }
379 }
378
380
379 void InvokeHandler(ResultHandlerInfo handler) {
381 void InvokeHandler(ResultHandlerInfo handler) {
380 if (m_error == null) {
382 if (m_error == null) {
381 try {
383 try {
382 if (handler.resultHandler != null)
384 if (handler.resultHandler != null)
383 handler.resultHandler(m_result);
385 handler.resultHandler(m_result);
384 } catch { }
386 } catch { }
385 }
387 }
386
388
387 if (m_error != null) {
389 if (m_error != null) {
388 try {
390 try {
389 if (handler.errorHandler != null)
391 if (handler.errorHandler != null)
390 handler.errorHandler(m_error);
392 handler.errorHandler(m_error);
391 } catch { }
393 } catch { }
392 }
394 }
393 }
395 }
394
396
395
397
396 }
398 }
397 }
399 }
General Comments 0
You need to be logged in to leave comments. Login now