diff --git a/Implab.Test/AsyncTests.cs b/Implab.Test/AsyncTests.cs --- a/Implab.Test/AsyncTests.cs +++ b/Implab.Test/AsyncTests.cs @@ -2,6 +2,7 @@ using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Reflection; using System.Threading; +using Implab.Parallels; namespace Implab.Test { @@ -70,6 +71,17 @@ namespace Implab.Test } [TestMethod] + public void FixErrorTest() { + var p = new Promise(); + + var p2 = p.Error(e => 101); + + p.Reject(new Exception()); + + Assert.AreEqual(p2.Join(), 101); + } + + [TestMethod] public void ChainTest () { var p1 = new Promise (); diff --git a/Implab.Test/PromiseHelper.cs b/Implab.Test/PromiseHelper.cs --- a/Implab.Test/PromiseHelper.cs +++ b/Implab.Test/PromiseHelper.cs @@ -1,4 +1,5 @@ -using System; +using Implab.Parallels; +using System; using System.Collections.Generic; using System.Linq; using System.Text; diff --git a/Implab.v11.suo b/Implab.v11.suo new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..89d4ac29ba1a27fd1eba1015282feda7105746af GIT binary patch literal 71680 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 diff --git a/Implab/AsyncPool.cs b/Implab/AsyncPool.cs deleted file mode 100644 --- a/Implab/AsyncPool.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Threading; - -namespace Implab { - /// - /// Класс для распаралеливания задач. - /// - /// - /// Используя данный класс и лямда выражения можно распараллелить - /// вычисления, для этого используется концепция обещаний. - /// - public static class AsyncPool { - - public static Promise Invoke(Func func) { - var p = new Promise(); - - ThreadPool.QueueUserWorkItem(param => { - try { - p.Resolve(func()); - } catch(Exception e) { - p.Reject(e); - } - }); - - return p; - } - } -} diff --git a/Implab/IPromise.cs b/Implab/IPromise.cs --- a/Implab/IPromise.cs +++ b/Implab/IPromise.cs @@ -24,11 +24,10 @@ namespace Implab } /// - /// Tries to cancel the promise or the complete chain. + /// Tries to cancel the the complete chain of promises. /// - /// Try to cancel the whole promise chain, the parent promise will be cancelled only if it has only one promise - /// - bool Cancel(bool dependencies); + /// true - if the promise has been cancelled, otherwise the promise will be resolved (or resolved already). + bool Cancel(); /// /// Registers handler for the case when the promise is cencelled. If the promise already cancelled the diff --git a/Implab/Implab.csproj b/Implab/Implab.csproj --- a/Implab/Implab.csproj +++ b/Implab/Implab.csproj @@ -38,12 +38,10 @@ - + - - - + \ No newline at end of file diff --git a/Implab/Parallels/AsyncPool.cs b/Implab/Parallels/AsyncPool.cs new file mode 100644 --- /dev/null +++ b/Implab/Parallels/AsyncPool.cs @@ -0,0 +1,28 @@ +using System; +using System.Threading; + +namespace Implab.Parallels { + /// + /// Класс для распаралеливания задач. + /// + /// + /// Используя данный класс и лямда выражения можно распараллелить + /// вычисления, для этого используется концепция обещаний. + /// + public static class AsyncPool { + + public static Promise Invoke(Func func) { + var p = new Promise(); + + ThreadPool.QueueUserWorkItem(param => { + try { + p.Resolve(func()); + } catch(Exception e) { + p.Reject(e); + } + }); + + return p; + } + } +} diff --git a/Implab/Promise.cs b/Implab/Promise.cs --- a/Implab/Promise.cs +++ b/Implab/Promise.cs @@ -7,7 +7,7 @@ using System.Threading; namespace Implab { public delegate void ErrorHandler(Exception e); - + public delegate T ErrorHandler(Exception e); public delegate void ResultHandler(T result); public delegate TNew ResultMapper(TSrc result); public delegate Promise ChainedOperation(TSrc result); @@ -126,52 +126,18 @@ namespace Implab { return Cancel(true); } - protected virtual void OnStateChanged() { - switch (m_state) { - case PromiseState.Resolved: - foreach (var resultHandlerInfo in m_resultHandlers) - try { - if (resultHandlerInfo.resultHandler != null) - resultHandlerInfo.resultHandler(m_result); - } catch (Exception e) { - try { - if (resultHandlerInfo.errorHandler != null) - resultHandlerInfo.errorHandler(e); - } catch { } - } - break; - case PromiseState.Cancelled: - foreach (var cancelHandler in m_cancelHandlers) - cancelHandler(); - break; - case PromiseState.Rejected: - foreach (var resultHandlerInfo in m_resultHandlers) - try { - if (resultHandlerInfo.errorHandler != null) - resultHandlerInfo.errorHandler(m_error); - } catch { } - break; - default: - throw new InvalidOperationException(String.Format("Promise entered an invalid state {0}", m_state)); - } - - m_resultHandlers = null; - m_cancelHandlers = null; - } - /// - /// Добавляет обработчики событий выполнения обещания. + /// Adds new handlers to this promise. /// - /// Обработчик успешного выполнения обещания. - /// Данному обработчику будет передан результат выполнения операции. - /// Обработчик ошибки. Данный обработчик получит - /// исключение возникшее при выполнении операции. - /// Само обещание + /// The handler of the successfully completed operation. + /// This handler will recieve an operation result as a parameter. + /// Handles an exception that may occur during the operation. + /// The new promise chained to this one. public Promise Then(ResultHandler success, ErrorHandler error) { if (success == null && error == null) return this; - var medium = new Promise(); + var medium = new Promise(this, true); var handlerInfo = new ResultHandlerInfo(); @@ -198,14 +164,99 @@ namespace Implab { return medium; } + /// + /// Adds new handlers to this promise. + /// + /// The handler of the successfully completed operation. + /// This handler will recieve an operation result as a parameter. + /// Handles an exception that may occur during the operation and returns the value which will be used as the result of the operation. + /// The new promise chained to this one. + public Promise Then(ResultHandler success, ErrorHandler error) { + if (success == null && error == null) + return this; + + var medium = new Promise(this, true); + + var handlerInfo = new ResultHandlerInfo(); + + if (success != null) + handlerInfo.resultHandler = x => { + success(x); + medium.Resolve(x); + }; + else + handlerInfo.resultHandler = medium.Resolve; + + if (error != null) + handlerInfo.errorHandler = x => { + try { + medium.Resolve(error(x)); + } catch { } + medium.Reject(x); + }; + else + handlerInfo.errorHandler = medium.Reject; + + AddHandler(handlerInfo); + + return medium; + } + + public Promise Then(ResultHandler success) { - return Then(success, null); + if (success == null) + return this; + + var medium = new Promise(this, true); + + var handlerInfo = new ResultHandlerInfo(); + + if (success != null) + handlerInfo.resultHandler = x => { + success(x); + medium.Resolve(x); + }; + else + handlerInfo.resultHandler = medium.Resolve; + + handlerInfo.errorHandler = medium.Reject; + + AddHandler(handlerInfo); + + return medium; } public Promise Error(ErrorHandler error) { return Then(null, error); } + /// + /// Handles error and allows to keep the promise. + /// + /// + /// If the specified handler throws an exception, this exception will be used to reject the promise. + /// + /// The error handler which returns the result of the promise. + /// New promise. + public Promise Error(ErrorHandler handler) { + if (handler == null) + return this; + + var medium = new Promise(this, true); + + AddHandler(new ResultHandlerInfo { + errorHandler = e => { + try { + medium.Resolve(handler(e)); + } catch (Exception e2) { + medium.Reject(e2); + } + } + }); + + return medium; + } + public Promise Anyway(Action handler) { if (handler == null) return this; @@ -295,9 +346,9 @@ namespace Implab { // notify chained operation that it's not needed medium.Cancelled(() => promise.Cancel()); promise.Then( - medium.Resolve, - medium.Reject - ); + x => medium.Resolve(x), + e => medium.Reject(e) + ); }, errorHandler = delegate(Exception e) { if (error != null) @@ -416,6 +467,39 @@ namespace Implab { } } + protected virtual void OnStateChanged() { + switch (m_state) { + case PromiseState.Resolved: + foreach (var resultHandlerInfo in m_resultHandlers) + try { + if (resultHandlerInfo.resultHandler != null) + resultHandlerInfo.resultHandler(m_result); + } catch (Exception e) { + try { + if (resultHandlerInfo.errorHandler != null) + resultHandlerInfo.errorHandler(e); + } catch { } + } + break; + case PromiseState.Cancelled: + foreach (var cancelHandler in m_cancelHandlers) + cancelHandler(); + break; + case PromiseState.Rejected: + foreach (var resultHandlerInfo in m_resultHandlers) + try { + if (resultHandlerInfo.errorHandler != null) + resultHandlerInfo.errorHandler(m_error); + } catch { } + break; + default: + throw new InvalidOperationException(String.Format("Promise entered an invalid state {0}", m_state)); + } + + m_resultHandlers = null; + m_cancelHandlers = null; + } + public bool IsExclusive { @@ -434,7 +518,7 @@ namespace Implab { } } - public bool Cancel(bool dependencies) { + protected bool Cancel(bool dependencies) { bool result; lock (m_lock) { @@ -450,7 +534,7 @@ namespace Implab { OnStateChanged(); if (dependencies && m_parent != null && m_parent.IsExclusive) { - m_parent.Cancel(true); + m_parent.Cancel(); } return result;