From 0101cbcd2009de9531283d7387b6ec3a3377bdfa Mon Sep 17 00:00:00 2001 From: Nathan Loewen Date: Fri, 17 Mar 2017 12:07:25 -0500 Subject: [PATCH 01/80] Implement Finally Implemented as described in issue #9. It's just a wrapper around existing functionality. --- Promise.cs | 34 +++++++++++ Promise_NonGeneric.cs | 34 +++++++++++ Tests/PromiseTests.cs | 96 +++++++++++++++++++++++++++++++ Tests/Promise_NonGeneric_Tests.cs | 96 +++++++++++++++++++++++++++++++ 4 files changed, 260 insertions(+) diff --git a/Promise.cs b/Promise.cs index e3ca483..3d94362 100644 --- a/Promise.cs +++ b/Promise.cs @@ -121,6 +121,18 @@ public interface IPromise /// Yields the value from the first promise that has resolved. /// IPromise ThenRace(Func> chain); + + /// + /// Add a finally callback. + /// Finally callbacks will always be called, even if any preceding promise is rejected, or encounters an error. + /// + IPromise Finally(Action onComplete); + + /// + /// Add a finally callback. + /// Finally callbacks will always be called, even if any preceding promise is rejected, or encounters an error. + /// + IPromise Finally(Func> onComplete); } /// @@ -787,5 +799,27 @@ public static IPromise Rejected(Exception ex) promise.Reject(ex); return promise; } + + public IPromise Finally(Action onComplete) + { + Promise promise = new Promise(); + promise.WithName(Name); + + this.Then((x) => { promise.Resolve(); }); + this.Catch((e) => { promise.Resolve(); }); + + return promise.Then(onComplete); + } + + public IPromise Finally(Func> onComplete) + { + Promise promise = new Promise(); + promise.WithName(Name); + + this.Then((x) => { promise.Resolve(); }); + this.Catch((e) => { promise.Resolve(); }); + + return promise.Then(onComplete); + } } } \ No newline at end of file diff --git a/Promise_NonGeneric.cs b/Promise_NonGeneric.cs index c66ec9b..3e900ab 100644 --- a/Promise_NonGeneric.cs +++ b/Promise_NonGeneric.cs @@ -113,6 +113,18 @@ public interface IPromise /// Returns a promise that resolves when the first of the promises has resolved. /// IPromise ThenRace(Func>> chain); + + /// + /// Add a finally callback. + /// Finally callbacks will always be called, even if any preceding promise is rejected, or encounters an error. + /// + IPromise Finally(Action onComplete); + + /// + /// Add a finally callback. + /// Finally callbacks will always be called, even if any preceding promise is rejected, or encounters an error. + /// + IPromise Finally(Func> onComplete); } /// @@ -886,6 +898,28 @@ public static IPromise Rejected(Exception ex) return promise; } + public IPromise Finally(Action onComplete) + { + Promise promise = new Promise(); + promise.WithName(Name); + + this.Then(() => { promise.Resolve(); }); + this.Catch((e) => { promise.Resolve(); }); + + return promise.Then(onComplete); + } + + public IPromise Finally(Func> onComplete) + { + Promise promise = new Promise(); + promise.WithName(Name); + + this.Then(() => { promise.Resolve(); }); + this.Catch((e) => { promise.Resolve(); }); + + return promise.Then(() => { return onComplete(); }); + } + /// /// Raises the UnhandledException event. /// diff --git a/Tests/PromiseTests.cs b/Tests/PromiseTests.cs index 837cb8f..2ee2caa 100644 --- a/Tests/PromiseTests.cs +++ b/Tests/PromiseTests.cs @@ -1172,5 +1172,101 @@ public void promises_have_sequential_ids() Assert.Equal(promise1.Id + 1, promise2.Id); } + + + [Fact] + public void finally_is_called_after_resolve() + { + var promise = new Promise(); + var callback = 0; + + promise.Finally(() => + { + ++callback; + }); + + promise.Resolve(0); + + Assert.Equal(1, callback); + } + + [Fact] + public void finally_is_called_after_reject() + { + var promise = new Promise(); + var callback = 0; + + promise.Finally(() => + { + ++callback; + }); + + promise.Reject(new Exception()); + + Assert.Equal(1, callback); + } + + [Fact] + public void resolved_chain_continues_after_finally() + { + var promise = new Promise(); + var callback = 0; + + promise.Finally(() => + { + ++callback; + }) + .Then(() => + { + ++callback; + }); + + promise.Resolve(0); + + Assert.Equal(2, callback); + } + + [Fact] + public void rejected_chain_continues_after_finally() + { + var promise = new Promise(); + var callback = 0; + + promise.Finally(() => + { + ++callback; + }) + .Then(() => + { + ++callback; + }); + + promise.Reject(new Exception()); + + Assert.Equal(2, callback); + } + + [Fact] + public void can_chain_promise_after_finally() + { + var promise = new Promise(); + var expectedValue = 5; + var callback = 0; + + promise.Finally(() => + { + ++callback; + return Promise.Resolved(expectedValue); + }) + .Then((x) => + { + ++callback; + Assert.Equal(expectedValue, x); + }); + + promise.Resolve(0); + + Assert.Equal(2, callback); + } } } diff --git a/Tests/Promise_NonGeneric_Tests.cs b/Tests/Promise_NonGeneric_Tests.cs index 123fe9c..3b8fe66 100644 --- a/Tests/Promise_NonGeneric_Tests.cs +++ b/Tests/Promise_NonGeneric_Tests.cs @@ -1119,5 +1119,101 @@ public void promises_have_sequential_ids() Assert.Equal(promise1.Id + 1, promise2.Id); } + + + [Fact] + public void finally_is_called_after_resolve() + { + var promise = new Promise(); + var callback = 0; + + promise.Finally(() => + { + ++callback; + }); + + promise.Resolve(); + + Assert.Equal(1, callback); + } + + [Fact] + public void finally_is_called_after_reject() + { + var promise = new Promise(); + var callback = 0; + + promise.Finally(() => + { + ++callback; + }); + + promise.Reject(new Exception()); + + Assert.Equal(1, callback); + } + + [Fact] + public void resolved_chain_continues_after_finally() + { + var promise = new Promise(); + var callback = 0; + + promise.Finally(() => + { + ++callback; + }) + .Then(() => + { + ++callback; + }); + + promise.Resolve(); + + Assert.Equal(2, callback); + } + + [Fact] + public void rejected_chain_continues_after_finally() + { + var promise = new Promise(); + var callback = 0; + + promise.Finally(() => + { + ++callback; + }) + .Then(() => + { + ++callback; + }); + + promise.Reject(new Exception()); + + Assert.Equal(2, callback); + } + + [Fact] + public void can_chain_promise_after_finally() + { + var promise = new Promise(); + var expectedValue = 5; + var callback = 0; + + promise.Finally(() => + { + ++callback; + return Promise.Resolved(expectedValue); + }) + .Then((x) => + { + ++callback; + Assert.Equal(expectedValue, x); + }); + + promise.Resolve(); + + Assert.Equal(2, callback); + } } } From a80a0da1223bc2bb3cff039909e56ad7ebf15e22 Mon Sep 17 00:00:00 2001 From: Nathan Loewen Date: Thu, 23 Mar 2017 12:07:05 -0500 Subject: [PATCH 02/80] Add Finally overload for chaining non-value promises --- Promise.cs | 17 +++++++++++++++++ Promise_NonGeneric.cs | 16 ++++++++++++++++ Tests/PromiseTests.cs | 23 ++++++++++++++++++++++- 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/Promise.cs b/Promise.cs index 3d94362..90e0345 100644 --- a/Promise.cs +++ b/Promise.cs @@ -128,6 +128,12 @@ public interface IPromise /// IPromise Finally(Action onComplete); + /// + /// Add a finally callback. + /// Finally callbacks will always be called, even if any preceding promise is rejected, or encounters an error. + /// + IPromise Finally(Func onResolved); + /// /// Add a finally callback. /// Finally callbacks will always be called, even if any preceding promise is rejected, or encounters an error. @@ -811,6 +817,17 @@ public IPromise Finally(Action onComplete) return promise.Then(onComplete); } + public IPromise Finally(Func onComplete) + { + Promise promise = new Promise(); + promise.WithName(Name); + + this.Then((x) => { promise.Resolve(); }); + this.Catch((e) => { promise.Resolve(); }); + + return promise.Then(onComplete); + } + public IPromise Finally(Func> onComplete) { Promise promise = new Promise(); diff --git a/Promise_NonGeneric.cs b/Promise_NonGeneric.cs index 3e900ab..b7d4ad0 100644 --- a/Promise_NonGeneric.cs +++ b/Promise_NonGeneric.cs @@ -120,6 +120,11 @@ public interface IPromise /// IPromise Finally(Action onComplete); + /// + /// Add a finally callback that chains a non-value promise. + /// + IPromise Finally(Func onResolved); + /// /// Add a finally callback. /// Finally callbacks will always be called, even if any preceding promise is rejected, or encounters an error. @@ -909,6 +914,17 @@ public IPromise Finally(Action onComplete) return promise.Then(onComplete); } + public IPromise Finally(Func onComplete) + { + Promise promise = new Promise(); + promise.WithName(Name); + + this.Then(() => { promise.Resolve(); }); + this.Catch((e) => { promise.Resolve(); }); + + return promise.Then(onComplete); + } + public IPromise Finally(Func> onComplete) { Promise promise = new Promise(); diff --git a/Tests/PromiseTests.cs b/Tests/PromiseTests.cs index 2ee2caa..c69c4f1 100644 --- a/Tests/PromiseTests.cs +++ b/Tests/PromiseTests.cs @@ -1247,7 +1247,7 @@ public void rejected_chain_continues_after_finally() } [Fact] - public void can_chain_promise_after_finally() + public void can_chain_promise_generic_after_finally() { var promise = new Promise(); var expectedValue = 5; @@ -1268,5 +1268,26 @@ public void can_chain_promise_after_finally() Assert.Equal(2, callback); } + + [Fact] + public void can_chain_promise_after_finally() + { + var promise = new Promise(); + var callback = 0; + + promise.Finally(() => + { + ++callback; + return Promise.Resolved(); + }) + .Then(() => + { + ++callback; + }); + + promise.Resolve(0); + + Assert.Equal(2, callback); + } } } From c804d71eb733ec03aef35d869da173f5ec8abb17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=20Mo=CC=88ller?= Date: Fri, 24 Mar 2017 18:11:32 +0100 Subject: [PATCH 03/80] Replace List by LinkedList to avoid problems when concurrent modifications are made. This is possible when an PromiseTimer is created as an result of an previous Resolved() PromiseTimer. This replacement should also provide some performance improvements when dealing with larger amounts of concurrent PromiseTimer. --- PromiseTimer.cs | 80 +++++++++++++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 36 deletions(-) diff --git a/PromiseTimer.cs b/PromiseTimer.cs index 8be369d..b19b361 100644 --- a/PromiseTimer.cs +++ b/PromiseTimer.cs @@ -103,7 +103,7 @@ public class PromiseTimer : IPromiseTimer /// /// Currently pending promises /// - private List waiting = new List(); + private LinkedList waiting = new LinkedList(); /// /// Resolve the returned promise once the time has elapsed @@ -136,26 +136,39 @@ public IPromise WaitUntil(Func predicate) predicate = predicate }; - waiting.Add(wait); + waiting.AddLast(wait); return promise; } public bool Cancel(IPromise promise) { - var wait = waiting.Find(w => w.pendingPromise.Id.Equals(promise.Id)); + var node = FindInWaiting(promise); - if (wait == null) + if (node == null) { - return false; + return false; } - wait.pendingPromise.Reject(new PromiseCancelledException("Promise was cancelled by user.")); - waiting.Remove(wait); + node.Value.pendingPromise.Reject(new PromiseCancelledException("Promise was cancelled by user.")); + waiting.Remove(node); return true; } + LinkedListNode FindInWaiting(IPromise promise) + { + for (var node = waiting.First; node != null; node = node.Next) + { + if (node.Value.pendingPromise.Id.Equals(promise.Id)) + { + return node; + } + } + + return null; + } + /// /// Update all pending promises. Must be called for the promises to progress and resolve at all. /// @@ -163,36 +176,31 @@ public void Update(float deltaTime) { curTime += deltaTime; - int i = 0; - while (i < waiting.Count) + for (var node = waiting.First; node != null; node = node.Next) { - var wait = waiting[i]; - - var newElapsedTime = curTime - wait.timeStarted; - wait.timeData.deltaTime = newElapsedTime - wait.timeData.elapsedTime; - wait.timeData.elapsedTime = newElapsedTime; - - bool result; - try - { - result = wait.predicate(wait.timeData); - } - catch (Exception ex) - { - wait.pendingPromise.Reject(ex); - waiting.RemoveAt(i); - continue; - } - - if (result) - { - wait.pendingPromise.Resolve(); - waiting.RemoveAt(i); - } - else - { - i++; - } + var wait = node.Value; + + var newElapsedTime = curTime - wait.timeStarted; + wait.timeData.deltaTime = newElapsedTime - wait.timeData.elapsedTime; + wait.timeData.elapsedTime = newElapsedTime; + + bool result; + try + { + result = wait.predicate(wait.timeData); + } + catch (Exception ex) + { + wait.pendingPromise.Reject(ex); + waiting.Remove(node); + continue; + } + + if (result) + { + wait.pendingPromise.Resolve(); + waiting.Remove(node); + } } } } From 0161343d38ec2bb87a050ade6a85968e3f43af2d Mon Sep 17 00:00:00 2001 From: Philip Warren Date: Mon, 27 Mar 2017 15:48:20 +1000 Subject: [PATCH 04/80] Added test to make sure all promises are updated correctly when a promise is removed during update. --- Tests/PromiseTimerTests.cs | 43 +++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/Tests/PromiseTimerTests.cs b/Tests/PromiseTimerTests.cs index e0fea02..b44589c 100644 --- a/Tests/PromiseTimerTests.cs +++ b/Tests/PromiseTimerTests.cs @@ -173,5 +173,46 @@ public void when_predicate_throws_exception_reject_promise() Assert.Equal(expectedException, caughtException); } + + [Fact] + public void all_promises_are_updated_when_a_pending_promise_is_removed_during_update() + { + var testObject = new PromiseTimer(); + + var p1Updates = 0; + var p2Updates = 0; + var p3Updates = 0; + + var p1 = testObject + .WaitUntil(timeData => + { + p1Updates++; + + return false; + }); + + var p2 = testObject + .WaitUntil(timeData => + { + p2Updates++; + testObject.Cancel(p1); + + return false; + }); + + var p3 = testObject + .WaitUntil(timeData => + { + p3Updates++; + + return false; + }); + + testObject.Update(0.01f); + + Assert.Equal(1, p1Updates); + Assert.Equal(1, p2Updates); + Assert.Equal(1, p3Updates); + } } -} +} \ No newline at end of file From 4bb66a6f79860893ac14b71b31c743f69a85a5af Mon Sep 17 00:00:00 2001 From: Philip Warren Date: Mon, 27 Mar 2017 17:08:58 +1000 Subject: [PATCH 05/80] Expanded on tests for removing pending promises during promise timer update and fixed issue due to linked list. --- PromiseTimer.cs | 93 +++++++++++++++++++++++--------------- Tests/PromiseTimerTests.cs | 53 ++++++++++++++++++++-- 2 files changed, 105 insertions(+), 41 deletions(-) diff --git a/PromiseTimer.cs b/PromiseTimer.cs index b19b361..530403a 100644 --- a/PromiseTimer.cs +++ b/PromiseTimer.cs @@ -4,7 +4,8 @@ namespace RSG { - public class PromiseCancelledException: Exception { + public class PromiseCancelledException : Exception + { /// /// Just create the exception /// @@ -147,7 +148,7 @@ public bool Cancel(IPromise promise) if (node == null) { - return false; + return false; } node.Value.pendingPromise.Reject(new PromiseCancelledException("Promise was cancelled by user.")); @@ -156,17 +157,17 @@ public bool Cancel(IPromise promise) return true; } - LinkedListNode FindInWaiting(IPromise promise) + LinkedListNode FindInWaiting(IPromise promise) { - for (var node = waiting.First; node != null; node = node.Next) - { - if (node.Value.pendingPromise.Id.Equals(promise.Id)) - { - return node; - } - } - - return null; + for (var node = waiting.First; node != null; node = node.Next) + { + if (node.Value.pendingPromise.Id.Equals(promise.Id)) + { + return node; + } + } + + return null; } /// @@ -176,33 +177,53 @@ public void Update(float deltaTime) { curTime += deltaTime; - for (var node = waiting.First; node != null; node = node.Next) + var node = waiting.First; + while (node != null) { - var wait = node.Value; - - var newElapsedTime = curTime - wait.timeStarted; - wait.timeData.deltaTime = newElapsedTime - wait.timeData.elapsedTime; - wait.timeData.elapsedTime = newElapsedTime; - - bool result; - try - { - result = wait.predicate(wait.timeData); - } - catch (Exception ex) - { - wait.pendingPromise.Reject(ex); - waiting.Remove(node); - continue; - } - - if (result) - { - wait.pendingPromise.Resolve(); - waiting.Remove(node); - } + var wait = node.Value; + + var newElapsedTime = curTime - wait.timeStarted; + wait.timeData.deltaTime = newElapsedTime - wait.timeData.elapsedTime; + wait.timeData.elapsedTime = newElapsedTime; + + bool result; + try + { + result = wait.predicate(wait.timeData); + } + catch (Exception ex) + { + wait.pendingPromise.Reject(ex); + + node = RemoveNode(node); + continue; + } + + if (result) + { + wait.pendingPromise.Resolve(); + + node = RemoveNode(node); + } + else + { + node = node.Next; + } } } + + /// + /// Removes the provided node and returns the next node in the list. + /// + private LinkedListNode RemoveNode(LinkedListNode node) + { + var currentNode = node; + node = node.Next; + + waiting.Remove(currentNode); + + return node; + } } } diff --git a/Tests/PromiseTimerTests.cs b/Tests/PromiseTimerTests.cs index b44589c..dee7646 100644 --- a/Tests/PromiseTimerTests.cs +++ b/Tests/PromiseTimerTests.cs @@ -175,7 +175,7 @@ public void when_predicate_throws_exception_reject_promise() } [Fact] - public void all_promises_are_updated_when_a_pending_promise_is_removed_during_update() + public void all_promises_are_updated_when_a_pending_promise_is_resolved_during_update() { var testObject = new PromiseTimer(); @@ -183,7 +183,7 @@ public void all_promises_are_updated_when_a_pending_promise_is_removed_during_up var p2Updates = 0; var p3Updates = 0; - var p1 = testObject + testObject .WaitUntil(timeData => { p1Updates++; @@ -191,16 +191,59 @@ public void all_promises_are_updated_when_a_pending_promise_is_removed_during_up return false; }); - var p2 = testObject + testObject .WaitUntil(timeData => { p2Updates++; - testObject.Cancel(p1); + + return true; + }); + + testObject + .WaitUntil(timeData => + { + p3Updates++; return false; }); - var p3 = testObject + testObject.Update(0.01f); + + Assert.Equal(1, p1Updates); + Assert.Equal(1, p2Updates); + Assert.Equal(1, p3Updates); + } + + [Fact] + public void all_promises_are_updated_when_a_pending_promise_is_canceled_during_update() + { + var testObject = new PromiseTimer(); + + var p1Updates = 0; + var p2Updates = 0; + var p3Updates = 0; + + var p1 = testObject + .WaitUntil(timeData => + { + p1Updates++; + + return false; + }); + + testObject + .WaitUntil(timeData => + { + p2Updates++; + + return true; + }) + .Then(() => + { + testObject.Cancel(p1); + }); + + testObject .WaitUntil(timeData => { p3Updates++; From 425bb1ca554ab10a23b1812b43568d7499c2aabd Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Wed, 18 Oct 2017 08:43:44 +1000 Subject: [PATCH 06/80] Removed RSG.Toolkit dependency because it was causing problems on PS4 Turned out we only needed one function from that whole library, LinqExts.FromItems, which was only 4 lines of code anyway. I copied this to EnumerableExt. --- EnumerableExt.cs | 13 ++++++++++- Examples/Example1/Example1.csproj | 4 ---- Examples/Example1/packages.config | 4 ---- Examples/Example2/Example2.csproj | 4 ---- Examples/Example2/packages.config | 4 ---- Examples/Example3/Example3.csproj | 4 ---- Examples/Example3/packages.config | 4 ---- Examples/Example4/Example4.csproj | 4 ---- Examples/Example4/packages.config | 4 ---- Examples/Example5/Example5.csproj | 4 ---- Examples/Example5/packages.config | 4 ---- Tests/Promise.Tests.csproj | 3 --- Tests/PromiseTests.cs | 21 +++++++++--------- Tests/Promise_NonGeneric_Tests.cs | 21 +++++++++--------- Tests/packages.config | 1 - .../RSG.Toolkit.1.0.0.0.nupkg | Bin 18575 -> 0 bytes .../lib/net35/RSG.Toolkit.dll | Bin 39424 -> 0 bytes 17 files changed, 32 insertions(+), 67 deletions(-) delete mode 100644 Examples/Example1/packages.config delete mode 100644 Examples/Example2/packages.config delete mode 100644 Examples/Example3/packages.config delete mode 100644 Examples/Example4/packages.config delete mode 100644 Examples/Example5/packages.config delete mode 100644 packages/RSG.Toolkit.1.0.0.0/RSG.Toolkit.1.0.0.0.nupkg delete mode 100644 packages/RSG.Toolkit.1.0.0.0/lib/net35/RSG.Toolkit.dll diff --git a/EnumerableExt.cs b/EnumerableExt.cs index 946b8f7..5e89156 100644 --- a/EnumerableExt.cs +++ b/EnumerableExt.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; namespace RSG.Promises @@ -41,5 +41,16 @@ public static void Each(this IEnumerable source, Action fn) index++; } } + + /// + /// Convert a variable length argument list of items to an enumerable. + /// + public static IEnumerable FromItems(params T[] items) + { + foreach (var item in items) + { + yield return item; + } + } } } diff --git a/Examples/Example1/Example1.csproj b/Examples/Example1/Example1.csproj index 9618aea..424c3ed 100644 --- a/Examples/Example1/Example1.csproj +++ b/Examples/Example1/Example1.csproj @@ -35,9 +35,6 @@ 4 - - ..\..\packages\RSG.Toolkit.1.0.0.0\lib\net35\RSG.Toolkit.dll - @@ -51,7 +48,6 @@ - diff --git a/Examples/Example1/packages.config b/Examples/Example1/packages.config deleted file mode 100644 index 2f690b6..0000000 --- a/Examples/Example1/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/Examples/Example2/Example2.csproj b/Examples/Example2/Example2.csproj index 53ae35a..da8be8b 100644 --- a/Examples/Example2/Example2.csproj +++ b/Examples/Example2/Example2.csproj @@ -35,9 +35,6 @@ 4 - - ..\..\packages\RSG.Toolkit.1.0.0.0\lib\net35\RSG.Toolkit.dll - @@ -51,7 +48,6 @@ - diff --git a/Examples/Example2/packages.config b/Examples/Example2/packages.config deleted file mode 100644 index 2f690b6..0000000 --- a/Examples/Example2/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/Examples/Example3/Example3.csproj b/Examples/Example3/Example3.csproj index 501fb64..da8885f 100644 --- a/Examples/Example3/Example3.csproj +++ b/Examples/Example3/Example3.csproj @@ -35,9 +35,6 @@ 4 - - ..\..\packages\RSG.Toolkit.1.0.0.0\lib\net35\RSG.Toolkit.dll - @@ -51,7 +48,6 @@ - diff --git a/Examples/Example3/packages.config b/Examples/Example3/packages.config deleted file mode 100644 index 2f690b6..0000000 --- a/Examples/Example3/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/Examples/Example4/Example4.csproj b/Examples/Example4/Example4.csproj index 61008ab..3eecaf6 100644 --- a/Examples/Example4/Example4.csproj +++ b/Examples/Example4/Example4.csproj @@ -35,9 +35,6 @@ 4 - - ..\..\packages\RSG.Toolkit.1.0.0.0\lib\net35\RSG.Toolkit.dll - @@ -51,7 +48,6 @@ - diff --git a/Examples/Example4/packages.config b/Examples/Example4/packages.config deleted file mode 100644 index 2f690b6..0000000 --- a/Examples/Example4/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/Examples/Example5/Example5.csproj b/Examples/Example5/Example5.csproj index 78f5126..05a6349 100644 --- a/Examples/Example5/Example5.csproj +++ b/Examples/Example5/Example5.csproj @@ -35,9 +35,6 @@ 4 - - ..\..\packages\RSG.Toolkit.1.0.0.0\lib\net35\RSG.Toolkit.dll - @@ -51,7 +48,6 @@ - diff --git a/Examples/Example5/packages.config b/Examples/Example5/packages.config deleted file mode 100644 index 2f690b6..0000000 --- a/Examples/Example5/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/Tests/Promise.Tests.csproj b/Tests/Promise.Tests.csproj index 5a2c1ab..6f1a72a 100644 --- a/Tests/Promise.Tests.csproj +++ b/Tests/Promise.Tests.csproj @@ -41,9 +41,6 @@ False ..\packages\Moq.4.2.1409.1722\lib\net35\Moq.dll - - ..\packages\RSG.Toolkit.1.0.0.0\lib\net35\RSG.Toolkit.dll - diff --git a/Tests/PromiseTests.cs b/Tests/PromiseTests.cs index c69c4f1..b66de93 100644 --- a/Tests/PromiseTests.cs +++ b/Tests/PromiseTests.cs @@ -1,7 +1,6 @@ -using Moq; +using Moq; using RSG; using RSG.Promises; -using RSG.Utils; using System; using System.Collections.Generic; using System.Linq; @@ -243,7 +242,7 @@ public void chain_multiple_promises_using_all() var completed = 0; promise - .ThenAll(i => LinqExts.FromItems(chainedPromise1, chainedPromise2).Cast>()) + .ThenAll(i => EnumerableExt.FromItems(chainedPromise1, chainedPromise2).Cast>()) .Then(result => { var items = result.ToArray(); @@ -282,7 +281,7 @@ public void chain_multiple_promises_using_all_that_are_resolved_out_of_order() var completed = 0; promise - .ThenAll(i => LinqExts.FromItems(chainedPromise1, chainedPromise2).Cast>()) + .ThenAll(i => EnumerableExt.FromItems(chainedPromise1, chainedPromise2).Cast>()) .Then(result => { var items = result.ToArray(); @@ -318,7 +317,7 @@ public void chain_multiple_promises_using_all_and_convert_to_non_value_promise() var completed = 0; promise - .ThenAll(i => LinqExts.FromItems(chainedPromise1, chainedPromise2).Cast()) + .ThenAll(i => EnumerableExt.FromItems(chainedPromise1, chainedPromise2).Cast()) .Then(() => { ++completed; @@ -345,7 +344,7 @@ public void combined_promise_is_resolved_when_children_are_resolved() var promise1 = new Promise(); var promise2 = new Promise(); - var all = Promise.All(LinqExts.FromItems>(promise1, promise2)); + var all = Promise.All(EnumerableExt.FromItems>(promise1, promise2)); var completed = 0; @@ -452,7 +451,7 @@ public void combined_promise_is_rejected_when_first_promise_is_rejected() var promise1 = new Promise(); var promise2 = new Promise(); - var all = Promise.All(LinqExts.FromItems>(promise1, promise2)); + var all = Promise.All(EnumerableExt.FromItems>(promise1, promise2)); all.Then(v => { @@ -502,7 +501,7 @@ public void combined_promise_is_rejected_when_second_promise_is_rejected() var promise1 = new Promise(); var promise2 = new Promise(); - var all = Promise.All(LinqExts.FromItems>(promise1, promise2)); + var all = Promise.All(EnumerableExt.FromItems>(promise1, promise2)); all.Then(v => { @@ -552,7 +551,7 @@ public void combined_promise_is_rejected_when_both_promises_are_rejected() var promise1 = new Promise(); var promise2 = new Promise(); - var all = Promise.All(LinqExts.FromItems>(promise1, promise2)); + var all = Promise.All(EnumerableExt.FromItems>(promise1, promise2)); all.Then(v => { @@ -599,7 +598,7 @@ public void combined_promise_of_multiple_types_is_rejected_when_both_promises_ar [Fact] public void combined_promise_is_resolved_if_there_are_no_promises() { - var all = Promise.All(LinqExts.Empty>()); + var all = Promise.All(EnumerableExt.Empty>()); var completed = 0; @@ -619,7 +618,7 @@ public void combined_promise_is_resolved_when_all_promises_are_already_resolved( var promise1 = Promise.Resolved(1); var promise2 = Promise.Resolved(1); - var all = Promise.All(LinqExts.FromItems(promise1, promise2)); + var all = Promise.All(EnumerableExt.FromItems(promise1, promise2)); var completed = 0; diff --git a/Tests/Promise_NonGeneric_Tests.cs b/Tests/Promise_NonGeneric_Tests.cs index 3b8fe66..a950327 100644 --- a/Tests/Promise_NonGeneric_Tests.cs +++ b/Tests/Promise_NonGeneric_Tests.cs @@ -1,7 +1,6 @@ -using Moq; +using Moq; using RSG; using RSG.Promises; -using RSG.Utils; using System; using System.Collections.Generic; using System.Linq; @@ -226,7 +225,7 @@ public void chain_multiple_promises_using_all() var completed = 0; promise - .ThenAll(() => LinqExts.FromItems(chainedPromise1, chainedPromise2).Cast()) + .ThenAll(() => EnumerableExt.FromItems(chainedPromise1, chainedPromise2).Cast()) .Then(() => ++completed); Assert.Equal(0, completed); @@ -256,7 +255,7 @@ public void chain_multiple_promises_using_all_that_are_resolved_out_of_order() var completed = 0; promise - .ThenAll(() => LinqExts.FromItems(chainedPromise1, chainedPromise2).Cast>()) + .ThenAll(() => EnumerableExt.FromItems(chainedPromise1, chainedPromise2).Cast>()) .Then(result => { var items = result.ToArray(); @@ -294,7 +293,7 @@ public void chain_multiple_value_promises_using_all_resolved_out_of_order() var completed = 0; promise - .ThenAll(() => LinqExts.FromItems(chainedPromise1, chainedPromise2).Cast>()) + .ThenAll(() => EnumerableExt.FromItems(chainedPromise1, chainedPromise2).Cast>()) .Then(result => { var items = result.ToArray(); @@ -326,7 +325,7 @@ public void combined_promise_is_resolved_when_children_are_resolved() var promise1 = new Promise(); var promise2 = new Promise(); - var all = Promise.All(LinqExts.FromItems(promise1, promise2)); + var all = Promise.All(EnumerableExt.FromItems(promise1, promise2)); var completed = 0; @@ -344,7 +343,7 @@ public void combined_promise_is_rejected_when_first_promise_is_rejected() var promise1 = new Promise(); var promise2 = new Promise(); - var all = Promise.All(LinqExts.FromItems(promise1, promise2)); + var all = Promise.All(EnumerableExt.FromItems(promise1, promise2)); all.Then(() => { @@ -369,7 +368,7 @@ public void combined_promise_is_rejected_when_second_promise_is_rejected() var promise1 = new Promise(); var promise2 = new Promise(); - var all = Promise.All(LinqExts.FromItems(promise1, promise2)); + var all = Promise.All(EnumerableExt.FromItems(promise1, promise2)); all.Then(() => { @@ -394,7 +393,7 @@ public void combined_promise_is_rejected_when_both_promises_are_rejected() var promise1 = new Promise(); var promise2 = new Promise(); - var all = Promise.All(LinqExts.FromItems(promise1, promise2)); + var all = Promise.All(EnumerableExt.FromItems(promise1, promise2)); all.Then(() => { @@ -416,7 +415,7 @@ public void combined_promise_is_rejected_when_both_promises_are_rejected() [Fact] public void combined_promise_is_resolved_if_there_are_no_promises() { - var all = Promise.All(LinqExts.Empty()); + var all = Promise.All(EnumerableExt.Empty()); var completed = 0; @@ -429,7 +428,7 @@ public void combined_promise_is_resolved_when_all_promises_are_already_resolved( var promise1 = Promise.Resolved(); var promise2 = Promise.Resolved(); - var all = Promise.All(LinqExts.FromItems(promise1, promise2)); + var all = Promise.All(EnumerableExt.FromItems(promise1, promise2)); var completed = 0; diff --git a/Tests/packages.config b/Tests/packages.config index b260bed..f51871d 100644 --- a/Tests/packages.config +++ b/Tests/packages.config @@ -1,7 +1,6 @@  - \ No newline at end of file diff --git a/packages/RSG.Toolkit.1.0.0.0/RSG.Toolkit.1.0.0.0.nupkg b/packages/RSG.Toolkit.1.0.0.0/RSG.Toolkit.1.0.0.0.nupkg deleted file mode 100644 index 16f87ec3f3909fed0a0f7aecbb63e05600366672..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18575 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 NrRN8g;Ga|T^%tEJ)Y$+4 diff --git a/packages/RSG.Toolkit.1.0.0.0/lib/net35/RSG.Toolkit.dll b/packages/RSG.Toolkit.1.0.0.0/lib/net35/RSG.Toolkit.dll deleted file mode 100644 index 4d7aea7c4061b63c55d5262f898bf542c47f1a93..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39424 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,We7ULCe3x?`#7FmP-Ar;nT0gwOOY(1qOj^H>j z+koN?f|nuqo(NBKJ+=Q}m))O5A9-G1kB9Z% zdh#&ZT>u^P2Vsy{$l;4Bz8ez<<3|&{nPA@T=Yo>^HG20coWFbYy)d9KAN ziBBBYbu(M0x12eB&dfQ)1f(13zSHnL{6;*-OyPY&J#EFKzf#EHV+`f1EOl2k4XzpW(A_!Pp8$ zRKDoJP+QN1(Ko)IAYi8#tgda?7CiYbjD8=2__4U)_Fk`o(kDHD?voE7eH7)O{7%!Q z*6CJIZm@{?q_xY6%4blH%0W$kr1)l=wV%;+t;6!e&PSY}JdHFezjer=#wF$jm$l=9 zEKk>TrKa7QzE9InX!@L{uW1?&{cA8NNljZqY`aX;>qDKP_sA{ia!~e$?hXazTS%kw z?^^QzWYDjJ2FfAXfk1Pn zR~Og1gz2(`)s3N29RJb6f=_bRkZ9#&{#f|TdWBMS8@v_d|u z&`k=h#A-qbA5-W;`HVu3Dzr-WDD<{M7va4tQckNT&x`R*GeYMnv|64}=yHYD%99Ei zdF$k}3K@Bq;^c(wdX(}qd0L?z3SA+eQ|O}#T`8Ye=ramkCC?~yK%sZx`?lor4TU!1 zTSA2P)=;xH*{je>95n=B_m>oU6>~m-8F&yQw$a&WCuB<1mT*FTSJfDqfuQs~dxo^T z%bbMVP_rVIkTdE(8c)df`d6`&{e9#S+~V$z?m>D#QXB@kPa%CX%Ji-1BS-@=maAf< z8A<;=w=NKrw_{Ap+e(~mW13Ellf#*D@?R8h!aefw`TW$)gWG%KO#fCn?Dkq6Lfdj} zzvup=N@rT$YIy~vudDbv@NE^}JOPP8#EtjHnI5X3p06v1A66J`{|WaB<#NLT_V@Nm zrj>#3qSZZ>Olt!_M*46i)2Aw%PLr&O|Ja|%y5b4s!NAXP zCWI}Weh6)Z3?H(r%k_ILgbxNHfDlCVb1+Z~C?U@(&)qV~>XI6H!-G~^IUFp#b}C7C z%VpMfK!;8@P`h(RSUpFw_2L>s$i>T4*1(mKLE^_Kz&? zSNJ&yZ~He38y)=|3|wpf#=^eZgWhld$>Pq1pM!zj;Ihg9fIepj?2C)g*X#;=brCvZ z*Vt={&~NRgGN{TqbqLgAUs9xOau(WG6rp*}N}Ffd{2UA{b1t{n%XaOp@yM&>wj#9I zzDj-Qc3I&;?0ZH&=0WWHCV9()*!M08<9w1_*!OE> zy$7-HTcqGY?0b(K@*ws-D@i;`WIOgfD;qtCeb35+MX1Hj$=U^ml6}8Up7tR2{d%dz z<1}(%-?z$a)$De$t2T$X$u5<5yX30AZQmeIc+hQt`X#IqZkI1s{Vu#+R(R0&s_G&G za*y)7UHY6}`#thag?7saocGu}2A+il-tzfTT% z(6#o1_WNbyVpDgmy~qB5eA$CuaGtVnmQHoU-SUF7&%Q-o@u06cFWI}KbBSs9HRq81 zK{@0>KXkrse@HsjVRy?9op0N>%S#H~E{*O_?GMY-IHseXY4>gWPMNLlOXyF4c6v~g z6LUTyPpdQUmYH^=^HJG|+cCC#zkQN(m#kP}pgC@%^KqH3j=ozK+G*!r+37(SxHFtj z%4-VUj=H(d{W5r=X}8K=`RQFGfBAsMCXf5qS{Me2uMdMuO3gInT%%16k)s*I_rb z-h=WA-R4n#KKeM??NNv`Zadn2RiPd7J5W9=|L(P;^k-%LX(j2;O2LCD{aN`k?!BOy zN`F?qYao^Wto+1-DE(QvOym5U5lROag^cuPxms>^e-)q|+ZbF$x~q%P0N8wwd+o|8W+v_n1x>3e0Z zu1BzZ{KddtdDe#@eXqRYL6p8%9%(kxLlut$dci;{eXo4kgD8EkROzZ_EU{NkQ^-i) zE9Wb;L$*|`ljr3LL#bZyywsjyEWy4%FVj4T(w~>Hx;C0|cwWvhkV=1E=6VpNKQDtG zCHwxo+@p|@{=Do{Xovg~()Ywt5hy?~~cOHoh6TvhrJ? zyud&zeV?rMAWGjSmwA+wzE27Y8R`2ZsH-8Nb@BzNGLW1y=I+23gkMAVcPxJ|AMond zpziPGZ;GqMI(b3v)~IN>ydWzy?)uQ*4S^yWBmKJU5NNA`WPhSI`hvV%R<~c4Y5X+p z_RHQO(4~t?dhVB(hCu5ye){cR7y@0X@zbyS@(}29jh}wqi$kC*G=BPZ;qxf{P3z^F zOl-ew^dS1ne%b0l9I^c}?R=$-jH!Lh*)K~Bq$9RpR(KFc41Mq@Ib!?e+X_+McC`DW zLObM<+I4b3PG3%*JLDOK)+uB(J0KekB)xUxq6g$ojhLqHi*k>F?@;T*^-eMTFmETtoNc~4k^7!UoXQLdNcaRDwixg z+E}xdGs1ry`MF9oJUQ;Z=al<^;mlt6bJ@_pt(f0yD;(aTN4Q}I2U^mS=K4~3ca-Jh zr*p@U#-y(&O7wTsvD{pba|qlKpNgvq*Hm0D<9Zcmb*JKL!Zj7w%eY>}z4EEJns80U z^)jwk@e2hP;%dUR8rRFXUd3tlskoYOO~v&xu2*qqeFLs0TsPu+8P}`$rqh4mYQl9C z*UPwGmD`1D<6TIv!G4wLr!{?2)8{ljpy^jMeO1%fk@h-oYWb&{{!-K5Bkgk{7Grq= z={gyw>13qW-~{>n@GfVumg91L_+!pI>*nyi&T{L{@B_}p!1p43D*UiB!}>z_G3RnC z?>^;pS>Fsl=Uj{0{Z6g?60I7wE%E>2Y*YN@GCAV9#KhfcN;BJ|S#3=LepciZcfR76 z7TaFwMxgCFoS1$N`@nffKjGeGaV=Xb^Re^2%i4m~>VE5kNbj=dW3Bo$WUZ4Ytp{TV z+$XKi$G+sQ*OR&RdMdLX+_pRG^?YurSDXlol3J)rSH`9znyxjw^OBq5~qMW z^+a$NEPOZ0l)h8iQ^uX zxJS5i-lHdodxWQndm!r&Fx=tpQCWNRWbtX`^R)7LS~=`fDf?8)KCRuS`s~ww?E_5^ zyUm}vOgVP@^u%$W@T_s4o;$vT-kpJ3_HLiItK|qDQf`No+acxin)dDu?cER5CU0rm z1C~_YAMCOYSf34k#j2=$z^=8JWG_?(by{XX(f!D-5Kn(~~c+@>j?F5tI? zB0_x*Sg|p8gf51*>tw#glhLK%aCcyZdg%(qtgyI?{sDS@D702F>$P^hSLQzYfYm-m z!kyaoRjr+2Ju;>-{HFB}V@?Xsu-+Uq4e1}o%ne6mc@^fnTwHa1xEqv*LIjbEM5`P2?hs0Pn$b9nMqBz9R9Mvs3%N%j!&qxe0FUv5RJ=SB}$P1OsdPb>au z#UHR1SFexmQ_Mb#XT2|J`6VqM(()mT=ew_I`86%Sq2)J}!_D%u>IdDM@s3knEMYSe z-YiWu4jQzlRS4}$h7W$HoF@Da8-7KF{n#V!&wvPB8b*v}bC)a&6 zHrYO_?%tSd(~@zU{!nYv6K@8LR$cUhmS3&gLL z=j%8khw3K8-?YAg^0o5)x(D5BdHpjarsz`KwW`sqaDCU(^+l*3{n+ zzg#Y@zdwGmYI3^$y7l?^G%e#@LHA$4=lbyK_+)vo{!j7Qia%iewtivsa`|KZe)O6e z{s3c_jwR&|+2OQEzs#v@!3peviWa;d@Fk>qX#wUI*@W7gWD&|WvKaUYvKr|*@?E4$ zl>Qo-jM`aRyGAir%I_)`Dd)xVv%n^2qn6vWJgDWHl=FRBKA`wFHMK0#JDQ%P>B&|M zIxNw0ho*xTwYo{mJGJ%!Ex)MgONu$7G~ZW@;M5B|ZE|*Pa!V@aB*iS!@)9jC)!L1U z=}^p|mT%JXPOZI9F%Kwazm{Lr@=IEKL^0o2j5sRSQMr!FRm@3>S)}D9THdJT4lNIA z`6eyjr{xEI{2AoInnr_r| zP}BXI9?>+J(6*WmYI>ihM>Lgct<`j+ruS*OUsI`3{+e#obWqd%njXx?*umJhV)6Kt`26@vJVAeN ze1H7cal2w%#ng&<6=Cdv@mmJi#|9*ZC){yB6(~(Xim#Kzp2a=u%Gei>9;>{)g0&-Q zUa$Nk_Us0KxOQjEl<{MtkY*!71dwinL|e8)rj0m(SB2&MkZa2>Ja@9?LwMd~%SRA9 zZ2S;!9nw$W*%8hJ@XW}@+0g`~d`1+OZ|O6wH}LL!SPsioNRP+{q~F6c;IRAv-(w8R zk0b**zXCoYKL=$*egVpeyoI(Ac^i}w`7J0T^6#MJZN=qCZR<*;u5}gCptS*M*lI%> zwK|Z-@%$ksW3AVb)`Bu7^`MMNHJ(<*WIQNi(g@0!Oax_IP6kih8Gte_DNy3>06gO| z4U}S^JAylZFJ9ZTiy4$yWFq3KXw1?o*Fne&>y%X za9`l_fqjAR1l|se37!_56I>Zw8(bgU9{fV^rJxm>97=_zhn9y{gq{dJ71|eiG4%b= zTX>InApGU<(eSB}_eH)LaiWu=GotIFw?!X`el7YhQ9E`@Y)0&o*j2FyVo%1NjeRZl zaQxfxstR|IcV6Y6SeSQtrH_R%;KzZV_#X?N!cQ2_JtMdGJA zH}G|HT-CU0aMj|fgQwNwYQQxX*En3`aZSM0h^qv0H3ip6xK75!k0XDt zdBDr7yu8}WYrVYQ%g1{8I4>VBuUhQ&czMlYU&qTos}Ux6^^M4hXvBMN6x8J5Cwh21 z{Z;%V4?o$fpW(qXJ@_mypX2F2$J2j~ z>hH=tkN#|r?i>$Z;N=TF{5w2&F>>~EvDcq-z4|2{eu;-~_2`#*`EoB`>E)}u{8BH! z+~aeV2XFB5%^tqX%dhqDc`xtx@&T`YhX>!}<+phFTfO{t4}XW3f5gi_?$zJx<@bB} zLtg%AFMrg_pG5wY^aM_~9t+M#zQO7VOhKN)bv?>ASaChUn4c8sk zPs1NZK8Wj1T(?-E$W6GPcI6!8??8S&@|DQfNA9xD!L<$WQ-Jp){~7Y1hVMbUzr}S2 z>c4>NMU~edb!BJtChH5x4@Nfte}}avwgK{Pv7U&ZW>74u#gO9hqKL8>riHm|%` z&4vam+HTm^+SYOPC?d*Pi`QVLDDin+xqM+&PTraA>M@3yC0BIy%Gw#ScBU+aSaf^U zCRy6IrPpgJmtLLeG3rs~dD-0BZM_*eudBO|$z?j%ijM(ZE^}eFC&L!G%*wv*LRW8h zMwI;gw){o8Y%lt>O_pT}e)*EN?mi6Ta6}o!g>73hdGYDNW^n=Uo3{5AGUa?!Mp>&8 zQK^LLCEacLG6u!Ewd7^|%jd*AzxHa_eG59FE)5!DRimLb@U84MnU$Hs)!9y1rl(U| zuWZX*lj%HnTTx7&V#=T)y}1a+?7}$H8=z%oOMCaW<(qrp=El^rvaQf@H9W11K{-3$ z5z1ziZ#8vQqn#Gf%s@p}V&EYPu@6HK0ItSRmGtX^g|bFRS?sAS1}=hBvAbLCAz*N6 zuB$+o_f?}s))d+bT^&BgxI|ADktEL+o>hxFiwhQG~`fip@+wx_d8=jWET ztvZcc&CulqPLyxkoRKziy@)NezGMK*_O71ZzJhe={L5$iavd4j)FYjlO>J;xqgK9< z%S%2(k&F!8UHQTcO=rptUA=H`$+o{+x=p}?A21TOnMhaQtm*aU0DY(k7}jmRU(;4n zfTEcK`vw+XFnz$&8uR?l5Fp`T-WBSN2ywyYwOIgv!lqtVE5(9lsC&y z%j&+KLf4i|%W`%&+q)){yRNGPp=n8W%Ozd;F8Fkb*RsB@&JyGs1bZ5W_DCP-eEX4?BUZ|3wV@h~`q1+2)>W|*>2t{2R6<=O(FCSINCZriG*vXJQY zlI>8{-qqbzC^I>h?@Z6oK9p?$US15N6Y zmKmbO*d!xmS!k~- z!F*;5&)l#9o8*OCHf+H1-qzi{4db_P&V~)wW0yCpRGdAU5O2p<939UnzCr$i5qvT; z(KEU8yE?I~l;)S$-1L$gQBzVk_u7_WR<`wE8SE_D1bg2#eZ9R|EFYaEhn`=y{iJiw zfjg>6yk)UyleseAptw_|k>(9CQd`L~?WJNzsn|J$yy)B9&`W85Ec5-{E|r-v!`B9@ zb5WBSWdkyUBT^cw8MB8mV0T8b`tWh$Eb_?A3Osy9od6Gx^)Mbo{H*9)u%z!#GiXH5 zw`S0UWnF1M5yeLl>U+^pOLDI{pWBqJ>foD7=A1dbq{GZ%o0&) z5v#7jU75dnt79UpmF+1;h@8&F3&F3aP_~NtDBP1|x;u5n^wugxlol%m%T_Ce%|4ks z>oS&RIhXm%O80DBb@Qq>4wLn6d!{T|bMpL!^EPZ)f?HJR-IMLvwk6x=ZRVF|x-*;6 zpIQDo$=uujOSOV zjy&NT<+pT1&B1~633ZASd6+xQ!`NW=ShA;o7p6BNG;kiLBYECg(W#4A-K8w7^S8S)RwX754_*a?mkG3Y-Pr7?mY``9gL}aXrez z*Yu2Gskv_&+3rlVThf;2W@4DOb4H@(jn)Cgso_n%MPV32T?mHbD1T)8QDo1ca1O{a zd}9?`TJH{5U0Tcmm{wy=?b?*d7s^m8vS#m!MXWP_X;-0mOtX9yyZ|5!$6Y_&kJZ^e zOj~bn&52xS>%uV>-P*r-=J?ELKQR}a(V=>shfS)#BhD3!8bereF&%YVbexrtKKWP1 zY;Q?09B5ZRhML`0`F-g8He+l=16`T0qw_G^{FSP}XrN&mOBDevb7;M#b0!yn($YC| z=%!#MHU&k>Gt0_no#-Mkq&t3BaNn{ty9J}CBj8~@3`eurEqVYgfl(gr%QM?$6XHdA zv_H<75*=gaJQ?NA9dnrXDgL?U8r)G8R4xCgh4PIjVs$Vs!*xQ@IzDYZJg-q29Q>I3 zC8b-Qzo@SrH)w2ajxSJe3iFF+HmX@CzUX;EadW1MF*5Sz2z6u>p1JA7n9Z7Q)a=1o zYiV1%Ru5Ge*@xd*ju->ppP_F?9d=|E4w4zz*yG}2g7+#q^oW>e;@KrwB6@Jro<-V? zXQ@ei*F2BsW=Y_aFhd4cJJKAWjBLeXnM02ABsc=P{RgRFt7Iql%)Va8bnd@GF73yN(t z)!?lsN=)kvA4QItKbEv)t;Ke27;9LywWM4Um{K=J8_^cD z7bY-_YLKHdrJ zR`Bdn3;SN*4Y(6pAH#wd|JV7JY@@SbTgf`?UkCn~8P2HCr@hX~Cg{_yFlQk>g8KfY z9;^gE_Arn5KwjNwK|ba4u%wN#gz99;lJn5E6cY+)pM*Ve%8Z;*D68{12Y8$M>_x+H z@Y&`hbYuidLVwDq#-_}8mW3wo21mvZQjaEalh;oZ3mNCK%0nv3=aif_nrE1(T0Wa( z&N5FQ>NQ#$&OQ@ojb0`qO2ls`>HkElG;MnwBr!htF_KzwG#RDK`cp%@lq^m?>r0(+k^keCa#l;0vB-98ZVgwnYD1DRX2r z_)khMj^!rk)1xuM-|>`>jIHAho6g9L@ao zXOT?kLMyZ9W4z!xQ>+Uys$44~-#zX8d*^Sz{M^qzX(LZSC1+BV<0xUdx{!5>gQoahGfE2koG8hZODY~x)y<(KhzuF}UEPe@>gL7>e$I|us+-YFOOoQF41ZNfni zpD$M06bu#IjIO%+F1*SZB-sD@sZVb{Xcb76x@CUy`>VsHn`Vf^-L zU}@{Q>y~MNuO7TBpfk@+;>Acr{OZAnFq@3Wc6N~v-?4#^u&ECh>jRiYO^rre2@9))Y|MB34~Kkj<#5zUsIGeOE65{BjE|PI zt1rUR?QFBUu=b|7R`558=}oFT`? zZ;FOc=Y*^sz*Im}A+l6Gke}?cX2E9-7Pi)TEKV-67#X|B*o}JJ z$AJkNrhKL>_)LLkH727^lOKo?6q3Y5}X}(^eDNK?dx2 z2$9i4*|Crdffx>`r%d4n6(KiVqNpTA3FIOHUoo7WsKp6bS=m$x(^smz8r$<5yXZIc z7MnB3_D0f*f%FuIn%CI=s9N+P&Xw6-MJ?^*1E%LTjRjY*JQ)_qi+qy!B^t7%N7*PV zE3*hx)3&p{6+v~kF-Or$tPB`4Oh`^#CtMQ>AS#>A`8837IfIIj4Na;Yfmc{|jRh8S zRj%6CgDI9Q8^>0$&0I|QiQeo2{d}d@0`XB!y!lGPh0+&KL1A>FxQHMw4E`&^8M0p{ z27iOM{}BKXq5zt95wrk|p!54!6g(?%A(DnzMh?SNFG7NK2eF`fUIZEs{s|!*VGHqW z9*v>kSbx1kJ8GeL?NXG~;gD0^+=M~r0yVEP2ymXwiHw-bGVJ5AYPK7Ob;x67W9ngP zF%ieC4OjsER+RzBK~Oi<8sib%sjgY)KxYb0u&GgdS79~685 zqXfMo#uQB0Hx1=GtN`>u3TJNQMBWjw8flao85@d*s~0q((qO~YiRz_|Hspegt8(0z zqDwRZ7BHKu&o@N`fo9OIfG{00jvd0grz`u3A!n_pP+j#!0Myr5z|661D8uEOZvQ8{ z(MDzUT9Q-O%Rq$crbe5~w$Dbx39EGCuScP5QEyV2L>ie;ah|WwJPysgAS7(8o&olq zpi*WmKoTmh<}~aEs7kVcDPFz6=dger7LY@)awzy5STG#6lEZeN0}D3k2M0BJ?DQK9 zvV^%Q)jJ|IL!}GHtwgxR4uTdzSTgH2In95oLKf|IKRJ*WIAxJS;i_t$Dy9?|Jo1Dr}xCzTUrdE^FR2c>q6TPxF z6m7y(0Ui+AY8^Ks3#vj9)Xb}_Uf{KGLvR&rJsTS_7d&Q-N>2;v{6&PKYoQ7Dlqi!B z?rNAPY)o-RiUqzY7nn$~z_S5BOw@Qc?Mbe~i%i>QE#;{Ix0Zt6 zS*{+*!QhK8Jtn{S>11-|^cnbBywfc?dFI@iXJ*>lI-5IYcFt*@-Feo$=D9Pc&upI4 zab{=d+*z5-^!8c!PCR~9bq0R(7yt0R9snv2g)rq}F~Ju_*4p)aw;gYK7oV))b)xL` zd8L)_J^Jglq_uJJ_j*6}D6S8QtX{Kp&9akDx@SuJEv=6&s+#}Uw}15%Oe-B{uV0Gy zDciGK*WPFex>98uOIU_yT^(Q4z&OsS^pm72t4V| z$936$k+%6Yg755^XO4Wr!Dlh+z}uWswk!Q=Ky00aJaXdMAfKEj@$DmX z(#x_r1tkwY0qO=#FR*P$dEUko&eFL&>G_P4vix)FSvX5a1jX3a6#L5Or#;Z2cw&h9 z6VV1_vIyUEw2Gs}bNf7Wp!R(1XwElDM?YBx$!noe$$s=be%IF@qf4~s{*z#~n}Igd zix=fpfu3GrPW^a>*j=Xi=yF Date: Wed, 18 Oct 2017 08:44:30 +1000 Subject: [PATCH 07/80] Added .vs to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index f571ce5..f033c24 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. +.vs # User-specific files *.suo From e1f3158609441d5e8bd08c4e1ede84a2480bbab9 Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Wed, 18 Oct 2017 08:53:54 +1000 Subject: [PATCH 08/80] Fixed issue with incorrect version of the test runner that was broken in VS 2017 --- Tests/Promise.Tests.csproj | 6 +++--- Tests/packages.config | 4 ++-- packages/repositories.config | 9 --------- 3 files changed, 5 insertions(+), 14 deletions(-) delete mode 100644 packages/repositories.config diff --git a/Tests/Promise.Tests.csproj b/Tests/Promise.Tests.csproj index 6f1a72a..8161139 100644 --- a/Tests/Promise.Tests.csproj +++ b/Tests/Promise.Tests.csproj @@ -1,6 +1,5 @@  - @@ -70,7 +69,9 @@ - + + Designer + @@ -80,7 +81,6 @@ - From b7a92a93f4f269199eb0bc7d5e3efdaed95c81be Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Thu, 16 Nov 2017 16:30:09 +1000 Subject: [PATCH 15/80] Updated readme --- README.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index de1a871..99de306 100644 --- a/README.md +++ b/README.md @@ -455,25 +455,31 @@ I'm starting to feel like we are defining behavior trees. ## PromiseTimer class -The promise timer is not part of the Promises/A+ standard but is a utility that makes promises a bit easier to use for operations that run across several frames, common in games. +The promise timer is not part of the Promises/A+ standard but is a utility that makes it possible to create promises that check if a condition is met each time the promise timer is updated. A common usage of this is in games where the promise timer is updated each frame. To use it, create an instance of the promise timer and call its `Update` method in your main loop: - class ExampleScript : MonoBehaviour + class Example { private IPromiseTimer promiseTimer; - void Start() + Example() { promiseTimer = new PromiseTimer(); } - void Update() + // Run once for every frame - equivilant to Update() in Unity + void MainLoop() { + // deltaTime is equal to the time since the last MainLoop promiseTimer.Update(Time.deltaTime); + + // Process your other logic here } } +Note that usually it is best to call `PromiseTimer.Update` *before* your other logic, otherwise you may have unintended behaviour like promises that are supposed to take a very short time resolving in the same update loop as they were created in. + ### PromiseTimer.WaitFor This method creates a promise that resolves after the specified amount of time in seconds has passed. Time is calculated as the sum of the delta values passed into `PromiseTimer.Update` From 4e65024104fa25c2f8c943a682db224373ee2e9f Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Thu, 16 Nov 2017 16:55:14 +1000 Subject: [PATCH 16/80] Updated readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 99de306..139f8cc 100644 --- a/README.md +++ b/README.md @@ -478,7 +478,7 @@ To use it, create an instance of the promise timer and call its `Update` method } } -Note that usually it is best to call `PromiseTimer.Update` *before* your other logic, otherwise you may have unintended behaviour like promises that are supposed to take a very short time resolving in the same update loop as they were created in. +Note that usually it is best to call `PromiseTimer.Update` *before* your other logic, otherwise you may have unintended behaviour such as promises that are supposed to take a very short time resolving in the same update loop as they were created in. ### PromiseTimer.WaitFor From 3dfd22c0fe77612e7101f826ac4f70e2bbc06689 Mon Sep 17 00:00:00 2001 From: Morgan Moon Date: Thu, 16 Nov 2017 13:57:24 -0600 Subject: [PATCH 17/80] README now includes a sub section under "PromiseTimer class" called "TimeData struct" that explains it --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 139f8cc..1e0d9ca 100644 --- a/README.md +++ b/README.md @@ -513,7 +513,13 @@ WaitUntil takes a predicate to check each update and resolves once the predicate WaitFor is exactly the same as WaitUntil except that it resolves when its predicate function returns false. Think of WaitUntil as running *until* its predicate returns true, and WaitWhile as running *while* its predicate returns true, stopping when it is false. +### TimeData struct +TimeData is passed to you as a paramter when using either PromiseTimer.WaitUntil or PromiseTimer.WaitWhile. It contains the following public fields: + + elapsedTime - The amount of time that has elapsed since the pending promise started running + deltaTime - The amount of time since the last time the pending promise was updated. + elapsedUpdates - The amount of times that PromiseTimer.Update() has been called since the pending promise started running ## Examples From b8fde52bd956b66eea31f5ccf9c75fa6825ab66a Mon Sep 17 00:00:00 2001 From: Morgan Moon Date: Thu, 16 Nov 2017 14:00:14 -0600 Subject: [PATCH 18/80] fixed formatting under TimeData struct section --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1e0d9ca..52a8d60 100644 --- a/README.md +++ b/README.md @@ -517,9 +517,9 @@ WaitFor is exactly the same as WaitUntil except that it resolves when its predic TimeData is passed to you as a paramter when using either PromiseTimer.WaitUntil or PromiseTimer.WaitWhile. It contains the following public fields: - elapsedTime - The amount of time that has elapsed since the pending promise started running - deltaTime - The amount of time since the last time the pending promise was updated. - elapsedUpdates - The amount of times that PromiseTimer.Update() has been called since the pending promise started running +- elapsedTime - The amount of time that has elapsed since the pending promise started running +- deltaTime - The amount of time since the last time the pending promise was updated. +- elapsedUpdates - The amount of times that PromiseTimer.Update() has been called since the pending promise started running ## Examples From f356d509af8d843d78211d2759abd4c103cbecb9 Mon Sep 17 00:00:00 2001 From: Morgan Moon Date: Thu, 16 Nov 2017 14:01:20 -0600 Subject: [PATCH 19/80] More formatting fixing under TimeData struct seciton in README (Sorry, never really do readme stuff) --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 52a8d60..71762ed 100644 --- a/README.md +++ b/README.md @@ -517,9 +517,12 @@ WaitFor is exactly the same as WaitUntil except that it resolves when its predic TimeData is passed to you as a paramter when using either PromiseTimer.WaitUntil or PromiseTimer.WaitWhile. It contains the following public fields: -- elapsedTime - The amount of time that has elapsed since the pending promise started running -- deltaTime - The amount of time since the last time the pending promise was updated. -- elapsedUpdates - The amount of times that PromiseTimer.Update() has been called since the pending promise started running +- elapsedTime + - The amount of time that has elapsed since the pending promise started running +- deltaTime + - The amount of time since the last time the pending promise was updated. +- elapsedUpdates + - The amount of times that PromiseTimer.Update() has been called since the pending promise started running ## Examples From 1b7eacc911b96e0b330e77632d11b547856e0cbf Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Fri, 17 Nov 2017 10:29:50 +1000 Subject: [PATCH 20/80] Added Nuget badge to readme Make it easier for people who find the library via GitHub to get it on Nuget --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 71762ed..a1b65f4 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# C-Sharp-Promise [![Build Status](https://travis-ci.org/Real-Serious-Games/C-Sharp-Promise.svg)](https://travis-ci.org/Real-Serious-Games/C-Sharp-Promise) # +# C-Sharp-Promise [![Build Status](https://travis-ci.org/Real-Serious-Games/C-Sharp-Promise.svg)](https://travis-ci.org/Real-Serious-Games/C-Sharp-Promise) [![NuGet](https://img.shields.io/nuget/v/RSG.Promise.svg)](https://www.nuget.org/packages/RSG.Promise/) # Promises/A+ logo Date: Wed, 22 Nov 2017 14:02:18 +1000 Subject: [PATCH 21/80] Fixed issue where a .Then after .Catch wouldn't be called --- Promise.cs | 14 +++++++++----- Tests/A+ Spec/2.2.cs | 12 ++++++++++++ 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/Promise.cs b/Promise.cs index 90e0345..b5424ed 100644 --- a/Promise.cs +++ b/Promise.cs @@ -441,8 +441,6 @@ public IPromise WithName(string name) /// public IPromise Catch(Action onRejected) { -// Argument.NotNull(() => onRejected); - var resultPromise = new Promise(); resultPromise.WithName(Name); @@ -453,9 +451,15 @@ public IPromise Catch(Action onRejected) Action rejectHandler = ex => { - onRejected(ex); - - resultPromise.Reject(ex); + try + { + onRejected(ex); + resultPromise.Resolve(default(PromisedT)); + } + catch(Exception cbEx) + { + resultPromise.Reject(cbEx); + } }; ActionHandlers(resultPromise, resolveHandler, rejectHandler); diff --git a/Tests/A+ Spec/2.2.cs b/Tests/A+ Spec/2.2.cs index dd5574c..30bdbc3 100644 --- a/Tests/A+ Spec/2.2.cs +++ b/Tests/A+ Spec/2.2.cs @@ -366,6 +366,18 @@ public void _when_promise1_is_resolved() Assert.Equal(1, promise1ThenHandler); Assert.Equal(1, promise2ThenHandler); } + + [Fact] + public void _when_promise1_is_rejected_with_no_value() + { + var callbackInvoked = false; + + new Promise((res, rej) => rej(new Exception())) + .Catch(ex => {}) + .Then(_ => callbackInvoked = true); + + Assert.True(callbackInvoked); + } } // 2.2.7.2 From d0dcea147023beb03368eeae1b6afdeb8fb830bc Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Wed, 22 Nov 2017 14:12:14 +1000 Subject: [PATCH 22/80] Support returning value from Catch handler (A+ spec/2.2.7.1) --- Promise.cs | 35 +++++++++++++++++++++++++++++++++++ Tests/A+ Spec/2.2.cs | 16 ++++++++++++++-- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/Promise.cs b/Promise.cs index b5424ed..ab7536e 100644 --- a/Promise.cs +++ b/Promise.cs @@ -46,6 +46,11 @@ public interface IPromise /// IPromise Catch(Action onRejected); + /// + /// Handle errors for the promise. + /// + IPromise Catch(Func onRejected); + /// /// Add a resolved callback that chains a value promise (optionally converting to a different value type). /// @@ -467,6 +472,36 @@ public IPromise Catch(Action onRejected) return resultPromise; } + /// + /// Handle errors for the promise. + /// + public IPromise Catch(Func onRejected) + { + var resultPromise = new Promise(); + resultPromise.WithName(Name); + + Action resolveHandler = v => + { + resultPromise.Resolve(v); + }; + + Action rejectHandler = ex => + { + try + { + resultPromise.Resolve(onRejected(ex)); + } + catch (Exception cbEx) + { + resultPromise.Reject(cbEx); + } + }; + + ActionHandlers(resultPromise, resolveHandler, rejectHandler); + + return resultPromise; + } + /// /// Add a resolved callback that chains a value promise (optionally converting to a different value type). /// diff --git a/Tests/A+ Spec/2.2.cs b/Tests/A+ Spec/2.2.cs index 30bdbc3..e66f787 100644 --- a/Tests/A+ Spec/2.2.cs +++ b/Tests/A+ Spec/2.2.cs @@ -331,7 +331,6 @@ public class then_must_return_a_promise { // 2.2.7.1 - // todo: Catch handler needs to be able to return a value. public class _If_either_onFulfilled_or_onRejected_returns_a_value_x_fulfill_promise_with_x { [Fact] @@ -373,11 +372,24 @@ public void _when_promise1_is_rejected_with_no_value() var callbackInvoked = false; new Promise((res, rej) => rej(new Exception())) - .Catch(ex => {}) + .Catch(_ => {}) .Then(_ => callbackInvoked = true); Assert.True(callbackInvoked); } + + [Fact] + public void _when_promise1_is_rejected_with_value() + { + var expectedValue = "Value returned from Catch"; + var actualValue = string.Empty; + + new Promise((res, rej) => rej(new Exception())) + .Catch(_ => expectedValue) + .Then(val => actualValue = val); + + Assert.Equal(expectedValue, actualValue); + } } // 2.2.7.2 From 42d1ab86d56c9a212980e08682d75b19e3a0bdfb Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Wed, 22 Nov 2017 16:46:52 +1000 Subject: [PATCH 23/80] Fixed examples and PromiseHelpers The previous commit changed the functionality of calling .Then after .Catch. Previously the last .Then would not be called if the first promise threw an exception, but now it will regardless. This fixes the logic to be the same as before. --- Examples/Example3/Program.cs | 12 ++++++------ Examples/Example4/Program.cs | 12 ++++++------ Examples/Example5/Program.cs | 12 ++++++------ PromiseHelpers.cs | 14 ++++++++------ 4 files changed, 26 insertions(+), 24 deletions(-) diff --git a/Examples/Example3/Program.cs b/Examples/Example3/Program.cs index 64f8d93..84ddb7a 100644 --- a/Examples/Example3/Program.cs +++ b/Examples/Example3/Program.cs @@ -31,12 +31,6 @@ static void Main(string[] args) .Find(html) .Select(link => link.Href) .ToArray(); - }) - .Catch(exception => // Catch any errors that happen during download or transform. - { - Console.WriteLine("Async operation errorred."); - Console.WriteLine(exception); - running = false; }) .Then(links => // Display the links that were extracted. { @@ -46,6 +40,12 @@ static void Main(string[] args) Console.WriteLine(link); } running = false; + }) + .Catch(exception => // Catch any errors that happen during download or transform. + { + Console.WriteLine("Async operation errorred."); + Console.WriteLine(exception); + running = false; }) .Done(); diff --git a/Examples/Example4/Program.cs b/Examples/Example4/Program.cs index 60ab547..252c389 100644 --- a/Examples/Example4/Program.cs +++ b/Examples/Example4/Program.cs @@ -34,18 +34,18 @@ static void Main(string[] args) .First(); // Grab the 6th link. }) .Then(firstLink => Download(firstLink)) // Follow the first link and download it. - .Catch(exception => // Catch any errors that happen during download or transform. - { - Console.WriteLine("Async operation errorred."); - Console.WriteLine(exception); - running = false; - }) .Then(html => // Display html from the link that was followed. { Console.WriteLine("Async operation completed."); Console.WriteLine(html.Substring(0, 250) + "..."); running = false; }) + .Catch(exception => // Catch any errors that happen during download or transform. + { + Console.WriteLine("Async operation errorred."); + Console.WriteLine(exception); + running = false; + }) .Done(); Console.WriteLine("Waiting"); diff --git a/Examples/Example5/Program.cs b/Examples/Example5/Program.cs index 9373ea1..0c6ced9 100644 --- a/Examples/Example5/Program.cs +++ b/Examples/Example5/Program.cs @@ -38,12 +38,6 @@ static void Main(string[] args) link => Download(link) // Download each link. ) )) - .Catch(exception => // Catch any errors that happen during download or transform. - { - Console.WriteLine("Async operation errorred."); - Console.WriteLine(exception); - running = false; - }) .Then(htmls => // Display html from the link that was followed. { Console.WriteLine("Async operation completed."); @@ -59,6 +53,12 @@ static void Main(string[] args) running = false; }) + .Catch(exception => // Catch any errors that happen during download or transform. + { + Console.WriteLine("Async operation errorred."); + Console.WriteLine(exception); + running = false; + }) .Done(); Console.WriteLine("Waiting"); diff --git a/PromiseHelpers.cs b/PromiseHelpers.cs index 647f789..5d4ec6c 100644 --- a/PromiseHelpers.cs +++ b/PromiseHelpers.cs @@ -19,8 +19,7 @@ public static IPromise> All(IPromise p1, IPromise var promise = new Promise>(); p1 - .Catch(e => promise.Reject(e)) - .Done(val => + .Then(val => { val1 = val; numUnresolved--; @@ -28,11 +27,12 @@ public static IPromise> All(IPromise p1, IPromise { promise.Resolve(Tuple.Create(val1, val2)); } - }); + }) + .Catch(e => promise.Reject(e)) + .Done(); p2 - .Catch(e => promise.Reject(e)) - .Done(val => + .Then(val => { val2 = val; numUnresolved--; @@ -40,7 +40,9 @@ public static IPromise> All(IPromise p1, IPromise { promise.Resolve(Tuple.Create(val1, val2)); } - }); + }) + .Catch(e => promise.Reject(e)) + .Done(); return promise; } From 7312474c9fda588cdb111de87fab4c1d850e2376 Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Wed, 22 Nov 2017 16:47:19 +1000 Subject: [PATCH 24/80] Made Catch(Action) return a non-generic promise If the "catch" isn't returning a value then we shouldn't be able to take a value in the next promise that's chained onto it. This is safer than the previous method, which was returning a new promise with default(PromisedT). This is a breaking change. --- Promise.cs | 39 ++++++++++++++++++++------------------- Tests/A+ Spec/2.2.cs | 2 +- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/Promise.cs b/Promise.cs index ab7536e..072f8f7 100644 --- a/Promise.cs +++ b/Promise.cs @@ -44,7 +44,7 @@ public interface IPromise /// /// Handle errors for the promise. /// - IPromise Catch(Action onRejected); + IPromise Catch(Action onRejected); /// /// Handle errors for the promise. @@ -444,14 +444,14 @@ public IPromise WithName(string name) /// /// Handle errors for the promise. /// - public IPromise Catch(Action onRejected) + public IPromise Catch(Action onRejected) { - var resultPromise = new Promise(); + var resultPromise = new Promise(); resultPromise.WithName(Name); - Action resolveHandler = v => + Action resolveHandler = _ => { - resultPromise.Resolve(v); + resultPromise.Resolve(); }; Action rejectHandler = ex => @@ -459,7 +459,7 @@ public IPromise Catch(Action onRejected) try { onRejected(ex); - resultPromise.Resolve(default(PromisedT)); + resultPromise.Resolve(); } catch(Exception cbEx) { @@ -729,14 +729,6 @@ public static IPromise> All(IEnumerable { promise - .Catch(ex => - { - if (resultPromise.CurState == PromiseState.Pending) - { - // If a promise errorred and the result promise is still pending, reject it. - resultPromise.Reject(ex); - } - }) .Then(result => { results[index] = result; @@ -748,6 +740,15 @@ public static IPromise> All(IEnumerable + { + if (resultPromise.CurState == PromiseState.Pending) + { + // If a promise errorred and the result promise is still pending, reject it. + resultPromise.Reject(ex); + } + return default(PromisedT); + }) .Done(); }); @@ -802,19 +803,19 @@ public static IPromise Race(IEnumerable> promises promisesArray.Each((promise, index) => { promise - .Catch(ex => + .Then(result => { if (resultPromise.CurState == PromiseState.Pending) { - // If a promise errorred and the result promise is still pending, reject it. - resultPromise.Reject(ex); + resultPromise.Resolve(result); } }) - .Then(result => + .Catch(ex => { if (resultPromise.CurState == PromiseState.Pending) { - resultPromise.Resolve(result); + // If a promise errorred and the result promise is still pending, reject it. + resultPromise.Reject(ex); } }) .Done(); diff --git a/Tests/A+ Spec/2.2.cs b/Tests/A+ Spec/2.2.cs index e66f787..bc79a4b 100644 --- a/Tests/A+ Spec/2.2.cs +++ b/Tests/A+ Spec/2.2.cs @@ -373,7 +373,7 @@ public void _when_promise1_is_rejected_with_no_value() new Promise((res, rej) => rej(new Exception())) .Catch(_ => {}) - .Then(_ => callbackInvoked = true); + .Then(() => callbackInvoked = true); Assert.True(callbackInvoked); } From 0a087f5edd6ea82d048dc4ec05380c7aef04a5ad Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Wed, 22 Nov 2017 16:58:51 +1000 Subject: [PATCH 25/80] Readme: fixed code examples that don't work with current version Examples had .Done being passed a value that was not returned from the previous .Catch, which doesn't work any more. I also removed references to .Done until the "Unhandled Errors" section to reduce the possibility of it creating confusion. --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index a1b65f4..2b073c4 100644 --- a/README.md +++ b/README.md @@ -163,7 +163,7 @@ There is another way to create a promise that replicates the JavaScript conventi The simplest usage is to register a completion handler to be invoked on completion of the async op: Download("http://www.google.com") - .Done(html => + .Then(html => Console.WriteLine(html) ); @@ -172,11 +172,11 @@ This snippet downloads the front page from Google and prints it to the console. For all but the most trivial applications you will also want to register an error hander: Download("http://www.google.com") + .Then(html => + Console.WriteLine(html) + ) .Catch(exception => Console.WriteLine("An exception occured while downloading!") - ) - .Done(html => - Console.WriteLine(html) ); The chain of processing for a promise ends as soon as an error/exception occurs. In this case when an error occurs the *Catch* handler would be called, but not the *Done* handler. If there is no error, then only *Done* is called. @@ -189,11 +189,11 @@ Multiple async operations can be chained one after the other using *Then*: .Then(html => return Download(ExtractFirstLink(html)) // Extract the first link and download it. ) + .Then(firstLinkHtml => + Console.WriteLine(firstLinkHtml) + ) .Catch(exception => Console.WriteLine("An exception occured while downloading!") - ) - .Done(firstLinkHtml => - Console.WriteLine(firstLinkHtml) ); Here we are chaining another download onto the end of the first download. The first link in the html is extracted and we then download that. *Then* expects the return value to be another promise. The chained promise can have a different *result type*. @@ -206,7 +206,7 @@ Sometimes you will want to simply transform or modify the resulting value withou .Then(html => return ExtractAllLinks(html)) // Extract all links and return an array of strings. ) - .Done(links => // The input here is an array of strings. + .Then(links => // The input here is an array of strings. foreach (var link in links) { Console.WriteLine(link); From 2018e84c5cdd6d97a38942fc7a5fd1e407a78376 Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Thu, 23 Nov 2017 16:35:26 +1000 Subject: [PATCH 26/80] Changed catch behaviour for non-generic promise to match generic one TODO: failing tests --- Promise_NonGeneric.cs | 22 +++++++++++++--------- Tests/A+ Spec/2.2.cs | 14 +++++++++++++- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/Promise_NonGeneric.cs b/Promise_NonGeneric.cs index b7d4ad0..92755f0 100644 --- a/Promise_NonGeneric.cs +++ b/Promise_NonGeneric.cs @@ -1,4 +1,4 @@ -using RSG.Promises; +using RSG.Promises; using System; using System.Collections.Generic; using System.Linq; @@ -511,9 +511,7 @@ public void Done(Action onResolved) /// public void Done() { - Catch(ex => - Promise.PropagateUnhandledException(this, ex) - ); + Catch(ex => PropagateUnhandledException(this, ex)); } /// @@ -542,9 +540,15 @@ public IPromise Catch(Action onRejected) Action rejectHandler = ex => { - onRejected(ex); - - resultPromise.Reject(ex); + try + { + onRejected(ex); + resultPromise.Resolve(); + } + catch (Exception callbackException) + { + resultPromise.Reject(callbackException); + } }; ActionHandlers(resultPromise, resolveHandler, rejectHandler); @@ -806,7 +810,7 @@ public static IPromise Sequence(params Func[] fns) public static IPromise Sequence(IEnumerable> fns) { return fns.Aggregate( - Promise.Resolved(), + Resolved(), (prevPromise, fn) => { return prevPromise.Then(() => fn()); @@ -820,7 +824,7 @@ public static IPromise Sequence(IEnumerable> fns) /// public IPromise ThenRace(Func> chain) { - return Then(() => Promise.Race(chain())); + return Then(() => Race(chain())); } /// diff --git a/Tests/A+ Spec/2.2.cs b/Tests/A+ Spec/2.2.cs index bc79a4b..c2387fe 100644 --- a/Tests/A+ Spec/2.2.cs +++ b/Tests/A+ Spec/2.2.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -390,6 +390,18 @@ public void _when_promise1_is_rejected_with_value() Assert.Equal(expectedValue, actualValue); } + + [Fact] + public void _when_non_generic_promise1_is_rejected() + { + var callbackInvoked = false; + + new Promise((res, rej) => rej(new Exception())) + .Catch(_ => {}) + .Then(() => callbackInvoked = true); + + Assert.True(callbackInvoked); + } } // 2.2.7.2 From f84e2a0ac743bb435e022e3c1842930de37c66c8 Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Thu, 23 Nov 2017 16:37:13 +1000 Subject: [PATCH 27/80] Fixed incorrect logic in some tests --- Tests/PromiseTests.cs | 2 +- Tests/Promise_NonGeneric_Tests.cs | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Tests/PromiseTests.cs b/Tests/PromiseTests.cs index b66de93..edf93f8 100644 --- a/Tests/PromiseTests.cs +++ b/Tests/PromiseTests.cs @@ -1040,7 +1040,7 @@ public void handled_exception_is_not_propagated_via_event() promise.Resolve(5); - Assert.Equal(1, eventRaised); + Assert.Equal(0, eventRaised); } finally { diff --git a/Tests/Promise_NonGeneric_Tests.cs b/Tests/Promise_NonGeneric_Tests.cs index a950327..fe64ad0 100644 --- a/Tests/Promise_NonGeneric_Tests.cs +++ b/Tests/Promise_NonGeneric_Tests.cs @@ -727,15 +727,16 @@ public void exception_thrown_in_sequence_rejects_the_promise() var ex = new Exception(); Promise - .Sequence(() => + .Sequence(() => { throw ex; }) - .Catch(e => { + .Then(() => ++completed) + .Catch(e => + { Assert.Equal(ex, e); ++errored; - }) - .Then(() => ++completed); + }); Assert.Equal(1, errored); Assert.Equal(0, completed); @@ -917,7 +918,7 @@ public void handled_exception_is_not_propagated_via_event() promise.Resolve(); - Assert.Equal(1, eventRaised); + Assert.Equal(0, eventRaised); } finally { From 729a4f8f2b0ffd257153c881e793eae3ee94e179 Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Fri, 24 Nov 2017 10:37:35 +1000 Subject: [PATCH 28/80] Simplified code in catch handlers --- Promise.cs | 12 +++--------- Promise_NonGeneric.cs | 5 +---- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/Promise.cs b/Promise.cs index 072f8f7..5dbbe8b 100644 --- a/Promise.cs +++ b/Promise.cs @@ -1,4 +1,4 @@ -using RSG.Promises; +using RSG.Promises; using System; using System.Collections.Generic; using System.Linq; @@ -449,10 +449,7 @@ public IPromise Catch(Action onRejected) var resultPromise = new Promise(); resultPromise.WithName(Name); - Action resolveHandler = _ => - { - resultPromise.Resolve(); - }; + Action resolveHandler = _ => resultPromise.Resolve(); Action rejectHandler = ex => { @@ -480,10 +477,7 @@ public IPromise Catch(Func onRejected) var resultPromise = new Promise(); resultPromise.WithName(Name); - Action resolveHandler = v => - { - resultPromise.Resolve(v); - }; + Action resolveHandler = v => resultPromise.Resolve(v); Action rejectHandler = ex => { diff --git a/Promise_NonGeneric.cs b/Promise_NonGeneric.cs index 92755f0..cda06cc 100644 --- a/Promise_NonGeneric.cs +++ b/Promise_NonGeneric.cs @@ -533,10 +533,7 @@ public IPromise Catch(Action onRejected) var resultPromise = new Promise(); resultPromise.WithName(Name); - Action resolveHandler = () => - { - resultPromise.Resolve(); - }; + Action resolveHandler = () => resultPromise.Resolve(); Action rejectHandler = ex => { From 2a78b1de11c641a45228ba4c3514ad03f6ca840a Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Fri, 24 Nov 2017 11:56:43 +1000 Subject: [PATCH 29/80] Modified Then with rejected callback to have the same functionality as Catch --- Promise.cs | 28 +++++++++++++++++++++++----- Tests/A+ Spec/2.2.cs | 38 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 59 insertions(+), 7 deletions(-) diff --git a/Promise.cs b/Promise.cs index 5dbbe8b..097fdfc 100644 --- a/Promise.cs +++ b/Promise.cs @@ -70,7 +70,10 @@ public interface IPromise /// Add a resolved callback and a rejected callback. /// The resolved callback chains a value promise (optionally converting to a different value type). /// - IPromise Then(Func> onResolved, Action onRejected); + IPromise Then( + Func> onResolved, + Func> onRejected + ); /// /// Add a resolved callback and a rejected callback. @@ -524,7 +527,10 @@ public IPromise Then(Action onResolved) /// Add a resolved callback and a rejected callback. /// The resolved callback chains a value promise (optionally converting to a different value type). /// - public IPromise Then(Func> onResolved, Action onRejected) + public IPromise Then( + Func> onResolved, + Func> onRejected + ) { // This version of the function must supply an onResolved. // Otherwise there is now way to get the converted value to pass to the resulting promise. @@ -545,12 +551,24 @@ public IPromise Then(Func rejectHandler = ex => { - if (onRejected != null) + if (onRejected == null) { - onRejected(ex); + resultPromise.Reject(ex); + return; } - resultPromise.Reject(ex); + try + { + onRejected(ex) + .Then( + (ConvertedT chainedValue) => resultPromise.Resolve(chainedValue), + callbackEx => resultPromise.Reject(callbackEx) + ); + } + catch (Exception callbackEx) + { + resultPromise.Reject(callbackEx); + } }; ActionHandlers(resultPromise, resolveHandler, rejectHandler); diff --git a/Tests/A+ Spec/2.2.cs b/Tests/A+ Spec/2.2.cs index c2387fe..f2b6e60 100644 --- a/Tests/A+ Spec/2.2.cs +++ b/Tests/A+ Spec/2.2.cs @@ -367,7 +367,7 @@ public void _when_promise1_is_resolved() } [Fact] - public void _when_promise1_is_rejected_with_no_value() + public void _when_promise1_is_rejected_with_no_value_in_catch() { var callbackInvoked = false; @@ -378,8 +378,26 @@ public void _when_promise1_is_rejected_with_no_value() Assert.True(callbackInvoked); } + public void _when_promise1_is_rejected_with_no_value_in_then() + { + var callbackInvoked = false; + var resolveHandlerInvoked = false; + var rejectHandlerInvoked = false; + + new Promise((res, rej) => rej(new Exception())) + .Then( + () => { resolveHandlerInvoked = true; }, + ex => { rejectHandlerInvoked = true; } + ) + .Then(() => callbackInvoked = true); + + Assert.True(callbackInvoked); + Assert.False(resolveHandlerInvoked); + Assert.True(rejectHandlerInvoked); + } + [Fact] - public void _when_promise1_is_rejected_with_value() + public void _when_promise1_is_rejected_with_value_in_catch() { var expectedValue = "Value returned from Catch"; var actualValue = string.Empty; @@ -391,6 +409,22 @@ public void _when_promise1_is_rejected_with_value() Assert.Equal(expectedValue, actualValue); } + [Fact] + public void _when_promise1_is_rejected_with_value_in_then() + { + var expectedValue = "Value returned from reject handler"; + var actualValue = string.Empty; + + new Promise((res, rej) => rej(new Exception())) + .Then( + _ => Promise.Resolved(string.Empty), + _ => Promise.Resolved(expectedValue) + ) + .Then(val => actualValue = val); + + Assert.Equal(expectedValue, actualValue); + } + [Fact] public void _when_non_generic_promise1_is_rejected() { From 4eea8a566cac1185ba2d282848c0d03a70071f54 Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Fri, 24 Nov 2017 13:37:29 +1000 Subject: [PATCH 30/80] Improved test coverage of Promise.Then --- Tests/PromiseTests.cs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/Tests/PromiseTests.cs b/Tests/PromiseTests.cs index edf93f8..364c4e8 100644 --- a/Tests/PromiseTests.cs +++ b/Tests/PromiseTests.cs @@ -1288,5 +1288,37 @@ public void can_chain_promise_after_finally() Assert.Equal(2, callback); } + + [Fact] + public void exception_in_reject_callback_is_caught_by_chained_catch() + { + var expectedException = new Exception("Expected"); + Exception actualException = null; + + new Promise((res, rej) => rej(new Exception())) + .Then( + _ => Promise.Resolved(null), + _ => throw expectedException + ) + .Catch(ex => actualException = ex); + + Assert.Equal(expectedException, actualException); + } + + [Fact] + public void rejected_reject_callback_is_caught_by_chained_catch() + { + var expectedException = new Exception("Expected"); + Exception actualException = null; + + new Promise((res, rej) => rej(new Exception())) + .Then( + _ => Promise.Resolved(null), + _ => Promise.Rejected(expectedException) + ) + .Catch(ex => actualException = ex); + + Assert.Equal(expectedException, actualException); + } } } From f65a67a6b9c94497ce2424fe28414a99a6e0dfa9 Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Mon, 4 Dec 2017 14:04:30 +1000 Subject: [PATCH 31/80] Removed unused and unnecessary methods from EnumerableExt --- EnumerableExt.cs | 15 --------------- Promise.cs | 2 +- Tests/PromiseTests.cs | 2 +- Tests/Promise_NonGeneric_Tests.cs | 2 +- 4 files changed, 3 insertions(+), 18 deletions(-) diff --git a/EnumerableExt.cs b/EnumerableExt.cs index 5e89156..1dcfd6e 100644 --- a/EnumerableExt.cs +++ b/EnumerableExt.cs @@ -8,21 +8,6 @@ namespace RSG.Promises /// public static class EnumerableExt { - public static IEnumerable Empty() - { - return new T[0]; - } - - public static IEnumerable LazyEach(this IEnumerable source, Action fn) - { - foreach (var item in source) - { - fn.Invoke(item); - - yield return item; - } - } - public static void Each(this IEnumerable source, Action fn) { foreach (var item in source) diff --git a/Promise.cs b/Promise.cs index 097fdfc..be19d08 100644 --- a/Promise.cs +++ b/Promise.cs @@ -730,7 +730,7 @@ public static IPromise> All(IEnumerable>.Resolved(EnumerableExt.Empty()); + return Promise>.Resolved(Enumerable.Empty()); } var remainingCount = promisesArray.Length; diff --git a/Tests/PromiseTests.cs b/Tests/PromiseTests.cs index 364c4e8..37c7ed8 100644 --- a/Tests/PromiseTests.cs +++ b/Tests/PromiseTests.cs @@ -598,7 +598,7 @@ public void combined_promise_of_multiple_types_is_rejected_when_both_promises_ar [Fact] public void combined_promise_is_resolved_if_there_are_no_promises() { - var all = Promise.All(EnumerableExt.Empty>()); + var all = Promise.All(Enumerable.Empty>()); var completed = 0; diff --git a/Tests/Promise_NonGeneric_Tests.cs b/Tests/Promise_NonGeneric_Tests.cs index fe64ad0..90b2026 100644 --- a/Tests/Promise_NonGeneric_Tests.cs +++ b/Tests/Promise_NonGeneric_Tests.cs @@ -415,7 +415,7 @@ public void combined_promise_is_rejected_when_both_promises_are_rejected() [Fact] public void combined_promise_is_resolved_if_there_are_no_promises() { - var all = Promise.All(EnumerableExt.Empty()); + var all = Promise.All(Enumerable.Empty()); var completed = 0; From 8cb7f52c83cc490fc777fffad18ced5923c1f812 Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Mon, 4 Dec 2017 14:07:10 +1000 Subject: [PATCH 32/80] Deleted obsolete Transform method --- Promise.cs | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/Promise.cs b/Promise.cs index be19d08..d5c3ce4 100644 --- a/Promise.cs +++ b/Promise.cs @@ -92,13 +92,6 @@ Func> onRejected /// IPromise Then(Func transform); - /// - /// Return a new promise with a different value. - /// May also change the type of the value. - /// - [Obsolete("Use Then instead")] - IPromise Transform(Func transform); - /// /// Chain an enumerable of promises, all of which must resolve. /// Returns a promise for a collection of the resolved results. @@ -659,17 +652,6 @@ public IPromise Then(Func transfo return Then(value => Promise.Resolved(transform(value))); } - /// - /// Return a new promise with a different value. - /// May also change the type of the value. - /// - [Obsolete("Use Then instead")] - public IPromise Transform(Func transform) - { -// Argument.NotNull(() => transform); - return Then(value => Promise.Resolved(transform(value))); - } - /// /// Helper function to invoke or register resolve/reject handlers. /// From ff33b9142f093f3899ad21efd08fda3cfc3d412c Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Mon, 4 Dec 2017 14:10:48 +1000 Subject: [PATCH 33/80] Bumped version number to 2.0 --- Properties/AssemblyInfo.cs | 4 ++-- README.md | 12 +++++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs index f7dc6f1..398772b 100644 --- a/Properties/AssemblyInfo.cs +++ b/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.3.0.0")] -[assembly: AssemblyFileVersion("1.3.0.0")] +[assembly: AssemblyVersion("2.0.0.0")] +[assembly: AssemblyFileVersion("2.0.0.0")] diff --git a/README.md b/README.md index 2b073c4..6032571 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,17 @@ If you are interested in using promises for game development and Unity please se ## Recent Updates -- 8 March 2015 +- v2.0 (4 December 2017) + - *Then* functions chained after a *Catch* are now run after the exception is handled rather than being terminated + - *Catch* can return a value which will be passed into the next *Then* + - The *onResolved* callback of *Then* can now also return a value which is passed to the next promise in the same way + - Added *elapsedUpdates* property to the TimeData struct used by PromiseTimer +- v1.3 (28 October 2017) + - Added Cancel method to PromiseTimer + - Implemented an overload of Promise.All that works on Tuples of multiple types + - Implemented Finally method + - Removed dependency on RSG.Toolkit +- v1.2 (8 March 2015) - *Transform* function has been renamed to *Then* (another overload of *Then*). ## Projects using this library From 880524bb00f5ffe275c5a3a456916252dd68f9ed Mon Sep 17 00:00:00 2001 From: HeLihua Date: Tue, 19 Dec 2017 11:02:25 +0800 Subject: [PATCH 34/80] Fix a typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6032571..f26b80c 100644 --- a/README.md +++ b/README.md @@ -521,7 +521,7 @@ WaitUntil takes a predicate to check each update and resolves once the predicate ### PromiseTimer.WaitWhile -WaitFor is exactly the same as WaitUntil except that it resolves when its predicate function returns false. Think of WaitUntil as running *until* its predicate returns true, and WaitWhile as running *while* its predicate returns true, stopping when it is false. +WaitWhile is exactly the same as WaitUntil except that it resolves when its predicate function returns false. Think of WaitUntil as running *until* its predicate returns true, and WaitWhile as running *while* its predicate returns true, stopping when it is false. ### TimeData struct From 2f478e48e8aa61bab91e183ddfb0d28bf7742e65 Mon Sep 17 00:00:00 2001 From: Jesse Rosalia Date: Sat, 20 Jan 2018 17:45:52 -0800 Subject: [PATCH 35/80] Changed Finally implementation to conform to spec tc39: When calling Finally that accepts an Action, resolved values are passed through the promise chain When calling Finally that accepts an Action, rejected promise exceptions are passed through the promise chain When an exception is thrown in Finally, the resulting promise rejects with the new exception (even if the previous promise was rejected) Added tests to test this behavior, and other behavior of tc39, e.g. throwing exceptions in finally Fixed a compiler error in exception_in_reject_callback_is_caught_by_chained_catch Fixed a handful of tests that had asserts in the promise callbacks after the counter that was used to verify success/failure --- Promise.cs | 33 +++++--- Promise_NonGeneric.cs | 24 ++++-- Tests/PromiseTests.cs | 133 ++++++++++++++++++++++++++++-- Tests/Promise_NonGeneric_Tests.cs | 121 ++++++++++++++++++++++++++- 4 files changed, 283 insertions(+), 28 deletions(-) diff --git a/Promise.cs b/Promise.cs index d5c3ce4..f8d669e 100644 --- a/Promise.cs +++ b/Promise.cs @@ -125,19 +125,22 @@ Func> onRejected /// /// Add a finally callback. - /// Finally callbacks will always be called, even if any preceding promise is rejected, or encounters an error. + /// Finally callbacks will always be called, even if any preceding promise is rejected, or encounters an error. + /// The returned promise will be resolved or rejected, as per the preceding promise. /// - IPromise Finally(Action onComplete); + IPromise Finally(Action onComplete); /// - /// Add a finally callback. - /// Finally callbacks will always be called, even if any preceding promise is rejected, or encounters an error. + /// Add a finally callback that chains a non-value promise. + /// Finally callbacks will always be called, even if any preceding promise is rejected, or encounters an error. + /// The state of the returning promise will be based on the new non-value promise, not the preceding (rejected or resolved) promise. /// IPromise Finally(Func onResolved); /// - /// Add a finally callback. - /// Finally callbacks will always be called, even if any preceding promise is rejected, or encounters an error. + /// Add a finally callback that chains a value promise (optionally converting to a different value type). + /// Finally callbacks will always be called, even if any preceding promise is rejected, or encounters an error. + /// The state of the returning promise will be based on the new value promise, not the preceding (rejected or resolved) promise. /// IPromise Finally(Func> onComplete); } @@ -840,15 +843,21 @@ public static IPromise Rejected(Exception ex) return promise; } - public IPromise Finally(Action onComplete) - { - Promise promise = new Promise(); + public IPromise Finally(Action onComplete) { + Promise promise = new Promise(); promise.WithName(Name); - this.Then((x) => { promise.Resolve(); }); - this.Catch((e) => { promise.Resolve(); }); + this.Then((x) => { promise.Resolve(x); }); + this.Catch((e) => { + try { + onComplete(); + promise.Reject(e); + } catch (Exception ne) { + promise.Reject(ne); + } + }); - return promise.Then(onComplete); + return promise.Then(_ => onComplete()); } public IPromise Finally(Func onComplete) diff --git a/Promise_NonGeneric.cs b/Promise_NonGeneric.cs index cda06cc..b2a0f5e 100644 --- a/Promise_NonGeneric.cs +++ b/Promise_NonGeneric.cs @@ -116,18 +116,22 @@ public interface IPromise /// /// Add a finally callback. - /// Finally callbacks will always be called, even if any preceding promise is rejected, or encounters an error. + /// Finally callbacks will always be called, even if any preceding promise is rejected, or encounters an error. + /// The returned promise will be resolved or rejected, as per the preceding promise. /// IPromise Finally(Action onComplete); /// /// Add a finally callback that chains a non-value promise. + /// Finally callbacks will always be called, even if any preceding promise is rejected, or encounters an error. + /// The state of the returning promise will be based on the new non-value promise, not the preceding (rejected or resolved) promise. /// IPromise Finally(Func onResolved); /// - /// Add a finally callback. - /// Finally callbacks will always be called, even if any preceding promise is rejected, or encounters an error. + /// Add a finally callback that chains a value promise (optionally converting to a different value type). + /// Finally callbacks will always be called, even if any preceding promise is rejected, or encounters an error. + /// The state of the returning promise will be based on the new value promise, not the preceding (rejected or resolved) promise. /// IPromise Finally(Func> onComplete); } @@ -904,15 +908,21 @@ public static IPromise Rejected(Exception ex) return promise; } - public IPromise Finally(Action onComplete) - { + public IPromise Finally(Action onComplete) { Promise promise = new Promise(); promise.WithName(Name); this.Then(() => { promise.Resolve(); }); - this.Catch((e) => { promise.Resolve(); }); + this.Catch((e) => { + try { + onComplete(); + promise.Reject(e); + } catch (Exception ne) { + promise.Reject(ne); + } + }); - return promise.Then(onComplete); + return promise.Then(() => onComplete()); } public IPromise Finally(Func onComplete) diff --git a/Tests/PromiseTests.cs b/Tests/PromiseTests.cs index 37c7ed8..4de56bf 100644 --- a/Tests/PromiseTests.cs +++ b/Tests/PromiseTests.cs @@ -1206,27 +1206,31 @@ public void finally_is_called_after_reject() } [Fact] + //tc39 public void resolved_chain_continues_after_finally() { var promise = new Promise(); var callback = 0; + var expectedValue = 42; promise.Finally(() => { ++callback; }) - .Then(() => + .Then((x) => { + Assert.Equal(expectedValue, x); ++callback; }); - promise.Resolve(0); + promise.Resolve(expectedValue); Assert.Equal(2, callback); } [Fact] - public void rejected_chain_continues_after_finally() + //tc39 + public void rejected_chain_rejects_after_finally() { var promise = new Promise(); var callback = 0; @@ -1235,7 +1239,7 @@ public void rejected_chain_continues_after_finally() { ++callback; }) - .Then(() => + .Catch(_ => { ++callback; }); @@ -1245,6 +1249,45 @@ public void rejected_chain_continues_after_finally() Assert.Equal(2, callback); } + [Fact] + public void rejected_chain_continues_after_finally_returning_non_value_promise() + { + var promise = new Promise(); + var callback = 0; + + promise.Finally(() => { + ++callback; + return Promise.Resolved(); + }) + .Then(() => { + ++callback; + }); + + promise.Reject(new Exception()); + + Assert.Equal(2, callback); + } + + [Fact] + public void rejected_chain_continues_after_finally_returning_value_promise() + { + var promise = new Promise(); + var callback = 0; + var expectedValue = 42; + promise.Finally(() => { + ++callback; + return Promise.Resolved(expectedValue); + }) + .Then((x) => { + Assert.Equal(expectedValue, (x)); + ++callback; + }); + + promise.Reject(new Exception()); + + Assert.Equal(2, callback); + } + [Fact] public void can_chain_promise_generic_after_finally() { @@ -1259,8 +1302,8 @@ public void can_chain_promise_generic_after_finally() }) .Then((x) => { - ++callback; Assert.Equal(expectedValue, x); + ++callback; }); promise.Resolve(0); @@ -1269,6 +1312,7 @@ public void can_chain_promise_generic_after_finally() } [Fact] + //tc39 public void can_chain_promise_after_finally() { var promise = new Promise(); @@ -1289,6 +1333,83 @@ public void can_chain_promise_after_finally() Assert.Equal(2, callback); } + [Fact] + //tc39 note: "a throw (or returning a rejected promise) in the finally callback will reject the new promise with that rejection reason." + public void exception_in_finally_callback_is_caught_by_chained_catch() + { + //NOTE: Also tests that the new exception is passed thru promise chain + + var promise = new Promise(); + var callback = 0; + var expectedException = new Exception("Expected"); + + // NOTE: typecast needed to call Finally(Action), and not Finally(Func) + promise.Finally((Action)(() => + { + ++callback; + throw expectedException; + })) + .Catch(ex => + { + Assert.Equal(expectedException, ex); + ++callback; + }); + + promise.Reject(new Exception()); + + Assert.Equal(2, callback); + } + + [Fact] + public void exception_in_finally_callback_returning_non_value_promise_is_caught_by_chained_catch() + { + //NOTE: Also tests that the new exception is passed thru promise chain + + var promise = new Promise(); + var callback = 0; + var expectedException = new Exception("Expected"); + + promise.Finally(new Func(() => + { + ++callback; + throw expectedException; + })) + .Catch(ex => + { + Assert.Equal(expectedException, ex); + ++callback; + }); + + promise.Reject(new Exception()); + + Assert.Equal(2, callback); + } + + [Fact] + public void exception_in_finally_callback_returning_value_promise_is_caught_by_chained_catch() + { + //NOTE: Also tests that the new exception is passed thru promise chain + + var promise = new Promise(); + var callback = 0; + var expectedException = new Exception("Expected"); + + promise.Finally(new Func>(() => + { + ++callback; + throw expectedException; + })) + .Catch(ex => + { + Assert.Equal(expectedException, ex); + ++callback; + }); + + promise.Reject(new Exception()); + + Assert.Equal(2, callback); + } + [Fact] public void exception_in_reject_callback_is_caught_by_chained_catch() { @@ -1298,7 +1419,7 @@ public void exception_in_reject_callback_is_caught_by_chained_catch() new Promise((res, rej) => rej(new Exception())) .Then( _ => Promise.Resolved(null), - _ => throw expectedException + _ => { throw expectedException; } ) .Catch(ex => actualException = ex); diff --git a/Tests/Promise_NonGeneric_Tests.cs b/Tests/Promise_NonGeneric_Tests.cs index 90b2026..4117a5a 100644 --- a/Tests/Promise_NonGeneric_Tests.cs +++ b/Tests/Promise_NonGeneric_Tests.cs @@ -1174,7 +1174,7 @@ public void resolved_chain_continues_after_finally() } [Fact] - public void rejected_chain_continues_after_finally() + public void rejected_chain_rejects_after_finally() { var promise = new Promise(); var callback = 0; @@ -1183,7 +1183,7 @@ public void rejected_chain_continues_after_finally() { ++callback; }) - .Then(() => + .Catch(_ => { ++callback; }); @@ -1193,6 +1193,121 @@ public void rejected_chain_continues_after_finally() Assert.Equal(2, callback); } + [Fact] + public void rejected_chain_continues_after_finally_returning_non_value_promise() { + var promise = new Promise(); + var callback = 0; + + promise.Finally(() => { + ++callback; + return Promise.Resolved(); + }) + .Then(() => { + ++callback; + }); + + promise.Reject(new Exception()); + + Assert.Equal(2, callback); + } + + [Fact] + public void rejected_chain_continues_after_finally_returning_value_promise() { + var promise = new Promise(); + var callback = 0; + var expectedValue = "foo"; + + promise.Finally(() => { + ++callback; + return Promise.Resolved("foo"); + }) + .Then((s) => { + Assert.Equal(expectedValue, x); + ++callback; + }); + + promise.Reject(new Exception()); + + Assert.Equal(2, callback); + } + + [Fact] + //tc39 note: "a throw (or returning a rejected promise) in the finally callback will reject the new promise with that rejection reason." + public void exception_in_finally_callback_is_caught_by_chained_catch() + { + //NOTE: Also tests that the new exception is passed thru promise chain + + var promise = new Promise(); + var callback = 0; + var expectedException = new Exception("Expected"); + + // NOTE: typecast needed to call Finally(Action), and not Finally(Func) + promise.Finally((Action)(() => + { + ++callback; + throw expectedException; + })) + .Catch(ex => + { + Assert.Equal(expectedException, ex); + ++callback; + }); + + promise.Reject(new Exception()); + + Assert.Equal(2, callback); + } + + [Fact] + public void exception_in_finally_callback_returning_non_value_promise_is_caught_by_chained_catch() + { + //NOTE: Also tests that the new exception is passed thru promise chain + + var promise = new Promise(); + var callback = 0; + var expectedException = new Exception("Expected"); + + promise.Finally(new Func(() => + { + ++callback; + throw expectedException; + })) + .Catch(ex => + { + Assert.Equal(expectedException, ex); + ++callback; + }); + + promise.Reject(new Exception()); + + Assert.Equal(2, callback); + } + + [Fact] + public void exception_in_finally_callback_returning_value_promise_is_caught_by_chained_catch() + { + //NOTE: Also tests that the new exception is passed thru promise chain + + var promise = new Promise(); + var callback = 0; + var expectedException = new Exception("Expected"); + + promise.Finally(new Func>(() => + { + ++callback; + throw expectedException; + })) + .Catch(ex => + { + Assert.Equal(expectedException, ex); + ++callback; + }); + + promise.Reject(new Exception()); + + Assert.Equal(2, callback); + } + [Fact] public void can_chain_promise_after_finally() { @@ -1207,8 +1322,8 @@ public void can_chain_promise_after_finally() }) .Then((x) => { - ++callback; Assert.Equal(expectedValue, x); + ++callback; }); promise.Resolve(); From 031b4a2b8efaa3e1722f3a06ae0e218f70275522 Mon Sep 17 00:00:00 2001 From: Jesse Rosalia Date: Sat, 20 Jan 2018 17:56:58 -0800 Subject: [PATCH 36/80] Fixed brace style on Finally methods --- Promise.cs | 3 ++- Promise_NonGeneric.cs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Promise.cs b/Promise.cs index f8d669e..eafda36 100644 --- a/Promise.cs +++ b/Promise.cs @@ -843,7 +843,8 @@ public static IPromise Rejected(Exception ex) return promise; } - public IPromise Finally(Action onComplete) { + public IPromise Finally(Action onComplete) + { Promise promise = new Promise(); promise.WithName(Name); diff --git a/Promise_NonGeneric.cs b/Promise_NonGeneric.cs index b2a0f5e..af1d6c9 100644 --- a/Promise_NonGeneric.cs +++ b/Promise_NonGeneric.cs @@ -908,7 +908,8 @@ public static IPromise Rejected(Exception ex) return promise; } - public IPromise Finally(Action onComplete) { + public IPromise Finally(Action onComplete) + { Promise promise = new Promise(); promise.WithName(Name); From d764f79814668ea0283d8499d3a8f184742d062c Mon Sep 17 00:00:00 2001 From: Jesse Rosalia Date: Sat, 20 Jan 2018 18:04:51 -0800 Subject: [PATCH 37/80] Fixed compiler error --- Tests/Promise_NonGeneric_Tests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Promise_NonGeneric_Tests.cs b/Tests/Promise_NonGeneric_Tests.cs index 4117a5a..065292a 100644 --- a/Tests/Promise_NonGeneric_Tests.cs +++ b/Tests/Promise_NonGeneric_Tests.cs @@ -1221,7 +1221,7 @@ public void rejected_chain_continues_after_finally_returning_value_promise() { ++callback; return Promise.Resolved("foo"); }) - .Then((s) => { + .Then((x) => { Assert.Equal(expectedValue, x); ++callback; }); From 3165f3f10cbb9b1872ebed6292d6378a7f06feaa Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Mon, 22 Jan 2018 10:16:54 +1000 Subject: [PATCH 38/80] Marked old versions of Finally as Obsolete This should help avoid confusion with the different behaviour --- Promise.cs | 4 ++++ Promise_NonGeneric.cs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/Promise.cs b/Promise.cs index eafda36..d2b70c6 100644 --- a/Promise.cs +++ b/Promise.cs @@ -135,6 +135,7 @@ Func> onRejected /// Finally callbacks will always be called, even if any preceding promise is rejected, or encounters an error. /// The state of the returning promise will be based on the new non-value promise, not the preceding (rejected or resolved) promise. /// + [Obsolete("Use new version of Finally that is TC39 compliant, or Catch and Then.")] IPromise Finally(Func onResolved); /// @@ -142,6 +143,7 @@ Func> onRejected /// Finally callbacks will always be called, even if any preceding promise is rejected, or encounters an error. /// The state of the returning promise will be based on the new value promise, not the preceding (rejected or resolved) promise. /// + [Obsolete("Use new version of Finally that is TC39 compliant, or Catch and Then.")] IPromise Finally(Func> onComplete); } @@ -861,6 +863,7 @@ public IPromise Finally(Action onComplete) return promise.Then(_ => onComplete()); } + [Obsolete("Use new version of Finally that is TC39 compliant, or Catch and Then.")] public IPromise Finally(Func onComplete) { Promise promise = new Promise(); @@ -872,6 +875,7 @@ public IPromise Finally(Func onComplete) return promise.Then(onComplete); } + [Obsolete("Use new version of Finally that is TC39 compliant, or Catch and Then.")] public IPromise Finally(Func> onComplete) { Promise promise = new Promise(); diff --git a/Promise_NonGeneric.cs b/Promise_NonGeneric.cs index af1d6c9..55f7da0 100644 --- a/Promise_NonGeneric.cs +++ b/Promise_NonGeneric.cs @@ -126,6 +126,7 @@ public interface IPromise /// Finally callbacks will always be called, even if any preceding promise is rejected, or encounters an error. /// The state of the returning promise will be based on the new non-value promise, not the preceding (rejected or resolved) promise. /// + [Obsolete("Use new version of Finally that is TC39 compliant, or Catch and Then.")] IPromise Finally(Func onResolved); /// @@ -133,6 +134,7 @@ public interface IPromise /// Finally callbacks will always be called, even if any preceding promise is rejected, or encounters an error. /// The state of the returning promise will be based on the new value promise, not the preceding (rejected or resolved) promise. /// + [Obsolete("Use new version of Finally that is TC39 compliant, or Catch and Then.")] IPromise Finally(Func> onComplete); } @@ -926,6 +928,7 @@ public IPromise Finally(Action onComplete) return promise.Then(() => onComplete()); } + [Obsolete("Use new version of Finally that is TC39 compliant, or Catch and Then.")] public IPromise Finally(Func onComplete) { Promise promise = new Promise(); @@ -937,6 +940,7 @@ public IPromise Finally(Func onComplete) return promise.Then(onComplete); } + [Obsolete("Use new version of Finally that is TC39 compliant, or Catch and Then.")] public IPromise Finally(Func> onComplete) { Promise promise = new Promise(); From e984b60a78bf884440fe215c8c21442fbd3a7b22 Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Tue, 23 Jan 2018 10:08:35 +1000 Subject: [PATCH 39/80] Signed assembly with a strong name --- C-Sharp-Promise.csproj | 6 ++++++ C-Sharp-Promise.snk | Bin 0 -> 596 bytes 2 files changed, 6 insertions(+) create mode 100644 C-Sharp-Promise.snk diff --git a/C-Sharp-Promise.csproj b/C-Sharp-Promise.csproj index e3d6bf2..0de980b 100644 --- a/C-Sharp-Promise.csproj +++ b/C-Sharp-Promise.csproj @@ -25,6 +25,12 @@ 4 false + + C-Sharp-Promise.snk + + + true + pdbonly true diff --git a/C-Sharp-Promise.snk b/C-Sharp-Promise.snk new file mode 100644 index 0000000000000000000000000000000000000000..b4ee3ec1055efa4bea0ab69566555371cc39c059 GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa500966S4$w02cJN(s)+-i^me77f$94|8!9AZ zuR`0G*N@D^D)38NuyIo*2u4JHkx3b%WD^oZ=e(daWOEoQ9b>@w%vDA!Q-7sFW1xVT zA6Eoy6wh6aZCPL?E32O4MXvf$g`xIkfzi}64AcQ5<6##JG0@rTci*oLh z8VOt?*&g4rn>M@P4UU)MYWO=zG&rL}=1)`axCIY(L1WO>H&peam54NbyG2a!n+|~p z@dS=BcvM{%IN9$953duchKrL8@6NQbZoWFHWWg{c0a~*p{4rONJ^J%36Z+y2gDTD9 z`?jRnX>O(F0c6;`Z9s^NlUK=*EP?(Nzi82PLRQeYrKksOFbWx0`8!*{lLaw?wf3R? zPD_&1K2G(KQS#iv{$rm&uKHDV_ZC4}esXEDK6t$(RKVq(^1I`yHcnVLts%4s<8QA( z4a_mgAp&XvAm2Qm=jfKq{)nx*)mA^Wu83!XcoI#sZi7s0PgDNtaD9$GO7)NANZ>47y`L)@~ zA`xGfBaB(Wmi{}x1M7MF_&t$@0;v;h#JU3c%FBP2^TUf9k$?96Y@)PjsMZjx8g@7| zw)oHe;@}qLxLPX#nm459`)mv)nSu|(di#4erKFraCHf&Z=QeR;?9yhnQ9|K2hWslf iM)CeAa=5cPH{&c+wvEraaaMpZA8;rYVu^gF&n|`#AQ!y= literal 0 HcmV?d00001 From b0d650ff3003bc0bf5c9c15d15fefc2400944b8d Mon Sep 17 00:00:00 2001 From: Alberto Alonso Date: Tue, 30 Jan 2018 23:26:56 +0200 Subject: [PATCH 40/80] Added support for progress reporting. --- Promise.cs | 136 +++++++++++++++++++++++++++++++++- Promise_NonGeneric.cs | 166 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 299 insertions(+), 3 deletions(-) diff --git a/Promise.cs b/Promise.cs index d2b70c6..583083b 100644 --- a/Promise.cs +++ b/Promise.cs @@ -1,4 +1,4 @@ -using RSG.Promises; +using RSG.Promises; using System; using System.Collections.Generic; using System.Linq; @@ -145,6 +145,20 @@ Func> onRejected /// [Obsolete("Use new version of Finally that is TC39 compliant, or Catch and Then.")] IPromise Finally(Func> onComplete); + + /// + /// Add a progress callback. + /// Progress callbacks will be called whenever the promise owner reports progress towards the resolution + /// of the promise. + /// + IPromise Progress(Action onProgress); + + /// + /// Add a progress function that can convert the progress value. + /// Progress callbacks will be called whenever the promise owner reports progress towards the resolution + /// of the promise. + /// + IPromise Progress(Func onProgress); } /// @@ -205,6 +219,11 @@ public class Promise : IPromise, IPendingPromise private List rejectHandlers; + /// + /// Progress handlers. + /// + private List progressHandlers; + /// /// Completed handlers that accept a value. /// @@ -295,6 +314,19 @@ private void AddResolveHandler(Action onResolved, IRejectable rejecta resolveRejectables.Add(rejectable); } + /// + /// Add a progress handler for this promise. + /// + private void AddProgressHandler(Action onProgress, IRejectable rejectable) + { + if (progressHandlers == null) + { + progressHandlers = new List(); + } + + progressHandlers.Add(new ProgressHandler() { callback = onProgress, rejectable = rejectable }); + } + /// /// Invoke a single handler. /// @@ -321,6 +353,7 @@ private void ClearHandlers() rejectHandlers = null; resolveCallbacks = null; resolveRejectables = null; + progressHandlers = null; } /// @@ -353,6 +386,17 @@ private void InvokeResolveHandlers(PromisedT value) ClearHandlers(); } + /// + /// Invoke all progress handlers. + /// + private void InvokeProgressHandlers(float progress) + { + if (progressHandlers != null) + { + progressHandlers.Each(handler => InvokeHandler(handler.callback, handler.rejectable, progress)); + } + } + /// /// Reject the promise with an exception. /// @@ -397,6 +441,19 @@ public void Resolve(PromisedT value) InvokeResolveHandlers(value); } + /// + /// Report progress on the promise. + /// + public void ReportProgress(float progress) + { + if (CurState != PromiseState.Pending) + { + throw new ApplicationException("Attempt to report progress on a promise that is already in state: " + CurState + ", a promise can only report progress when it is still in state: " + PromiseState.Pending); + } + + InvokeProgressHandlers(progress); + } + /// /// Completes the promise. /// onResolved is called on successful completion. @@ -466,6 +523,7 @@ public IPromise Catch(Action onRejected) }; ActionHandlers(resultPromise, resolveHandler, rejectHandler); + ProgressHandlers(resultPromise, v => resultPromise.ReportProgress(v)); return resultPromise; } @@ -493,6 +551,7 @@ public IPromise Catch(Func onRejected) }; ActionHandlers(resultPromise, resolveHandler, rejectHandler); + ProgressHandlers(resultPromise, v => resultPromise.ReportProgress(v)); return resultPromise; } @@ -570,6 +629,7 @@ Func> onRejected }; ActionHandlers(resultPromise, resolveHandler, rejectHandler); + ProgressHandlers(resultPromise, v => resultPromise.ReportProgress(v)); return resultPromise; } @@ -610,6 +670,7 @@ public IPromise Then(Func onResolved, Action onR }; ActionHandlers(resultPromise, resolveHandler, rejectHandler); + ProgressHandlers(resultPromise, v => resultPromise.ReportProgress(v)); return resultPromise; } @@ -643,6 +704,7 @@ public IPromise Then(Action onResolved, Action }; ActionHandlers(resultPromise, resolveHandler, rejectHandler); + ProgressHandlers(resultPromise, v => resultPromise.ReportProgress(v)); return resultPromise; } @@ -677,6 +739,17 @@ private void ActionHandlers(IRejectable resultPromise, Action resolve } } + /// + /// Helper function to invoke or register progress handlers. + /// + private void ProgressHandlers(IRejectable resultPromise, Action progressHandler) + { + if (CurState == PromiseState.Pending) + { + AddProgressHandler(progressHandler, resultPromise); + } + } + /// /// Chain an enumerable of promises, all of which must resolve. /// Returns a promise for a collection of the resolved results. @@ -722,6 +795,7 @@ public static IPromise> All(IEnumerable>(); resultPromise.WithName("All"); @@ -748,6 +822,11 @@ public static IPromise> All(IEnumerable + { + progress[index] = v; + resultPromise.ReportProgress(progress.Average()); + }) .Done(); }); @@ -799,6 +878,8 @@ public static IPromise Race(IEnumerable> promises var resultPromise = new Promise(); resultPromise.WithName("Race"); + var progress = new float[promisesArray.Length]; + promisesArray.Each((promise, index) => { promise @@ -817,6 +898,11 @@ public static IPromise Race(IEnumerable> promises resultPromise.Reject(ex); } }) + .Progress(v => + { + progress[index] = v; + resultPromise.ReportProgress(progress.Max()); + }) .Done(); }); @@ -886,5 +972,51 @@ public IPromise Finally(Func> onCom return promise.Then(onComplete); } + + public IPromise Progress(Action onProgress) + { + var resultPromise = new Promise(); + resultPromise.WithName(Name); + + this.Then((x) => { resultPromise.Resolve(x); }); + this.Catch((e) => { resultPromise.Reject(e); }); + + Action progressHandler = v => + { + if (onProgress != null) + { + onProgress(v); + } + + resultPromise.ReportProgress(v); + }; + + ProgressHandlers(resultPromise, progressHandler); + + return resultPromise; + } + + public IPromise Progress(Func onProgress) + { + var resultPromise = new Promise(); + resultPromise.WithName(Name); + + this.Then((x) => { resultPromise.Resolve(x); }); + this.Catch((e) => { resultPromise.Reject(e); }); + + Action progressHandler = v => + { + if (onProgress != null) + { + v = onProgress(v); + } + + resultPromise.ReportProgress(v); + }; + + ProgressHandlers(resultPromise, progressHandler); + + return resultPromise; + } } -} \ No newline at end of file +} diff --git a/Promise_NonGeneric.cs b/Promise_NonGeneric.cs index 55f7da0..263fb50 100644 --- a/Promise_NonGeneric.cs +++ b/Promise_NonGeneric.cs @@ -1,4 +1,4 @@ -using RSG.Promises; +using RSG.Promises; using System; using System.Collections.Generic; using System.Linq; @@ -136,6 +136,20 @@ public interface IPromise /// [Obsolete("Use new version of Finally that is TC39 compliant, or Catch and Then.")] IPromise Finally(Func> onComplete); + + /// + /// Add a progress callback. + /// Progress callbacks will be called whenever the promise owner reports progress towards the resolution + /// of the promise. + /// + IPromise Progress(Action onProgress); + + /// + /// Add a progress function that can convert the progress value. + /// Progress callbacks will be called whenever the promise owner reports progress towards the resolution + /// of the promise. + /// + IPromise Progress(Func onProgress); } /// @@ -205,6 +219,19 @@ public struct RejectHandler public IRejectable rejectable; } + public struct ProgressHandler + { + /// + /// Callback fn. + /// + public Action callback; + + /// + /// The promise that is rejected when there is an error while invoking the handler. + /// + public IRejectable rejectable; + } + /// /// Implements a non-generic C# promise, this is a promise that simply resolves without delivering a value. /// https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise @@ -277,6 +304,11 @@ public struct ResolveHandler /// private List resolveHandlers; + /// + /// Progress handlers. + /// + private List progressHandlers; + /// /// ID of the promise, useful for debugging. /// @@ -369,6 +401,19 @@ private void AddResolveHandler(Action onResolved, IRejectable rejectable) }); } + /// + /// Add a progress handler for this promise. + /// + private void AddProgressHandler(Action onProgress, IRejectable rejectable) + { + if (progressHandlers == null) + { + progressHandlers = new List(); + } + + progressHandlers.Add(new ProgressHandler() { callback = onProgress, rejectable = rejectable }); + } + /// /// Invoke a single error handler. /// @@ -405,6 +450,24 @@ private void InvokeResolveHandler(Action callback, IRejectable rejectable) } } + /// + /// Invoke a single progress handler. + /// + private void InvokeProgressHandler(Action callback, IRejectable rejectable, float progress) + { +// Argument.NotNull(() => callback); +// Argument.NotNull(() => rejectable); + + try + { + callback(progress); + } + catch (Exception ex) + { + rejectable.Reject(ex); + } + } + /// /// Helper function clear out all handlers after resolution or rejection. /// @@ -412,6 +475,7 @@ private void ClearHandlers() { rejectHandlers = null; resolveHandlers = null; + progressHandlers = null; } /// @@ -442,6 +506,17 @@ private void InvokeResolveHandlers() ClearHandlers(); } + /// + /// Invoke all progress handlers. + /// + private void InvokeProgressHandlers(float progress) + { + if (progressHandlers != null) + { + progressHandlers.Each(handler => InvokeProgressHandler(handler.callback, handler.rejectable, progress)); + } + } + /// /// Reject the promise with an exception. /// @@ -486,6 +561,21 @@ public void Resolve() InvokeResolveHandlers(); } + + /// + /// Report progress on the promise. + /// + public void ReportProgress(float progress) + { + if (CurState != PromiseState.Pending) + { + throw new ApplicationException("Attempt to report progress on a promise that is already in state: " + CurState + ", a promise can only report progress when it is still in state: " + PromiseState.Pending); + } + + InvokeProgressHandlers(progress); + } + + /// /// Completes the promise. /// onResolved is called on successful completion. @@ -555,6 +645,7 @@ public IPromise Catch(Action onRejected) }; ActionHandlers(resultPromise, resolveHandler, rejectHandler); + ProgressHandlers(resultPromise, v => resultPromise.ReportProgress(v)); return resultPromise; } @@ -617,6 +708,7 @@ public IPromise Then(Func> onResolv }; ActionHandlers(resultPromise, resolveHandler, rejectHandler); + ProgressHandlers(resultPromise, v => resultPromise.ReportProgress(v)); return resultPromise; } @@ -657,6 +749,7 @@ public IPromise Then(Func onResolved, Action onRejected) }; ActionHandlers(resultPromise, resolveHandler, rejectHandler); + ProgressHandlers(resultPromise, v => resultPromise.ReportProgress(v)); return resultPromise; } @@ -690,6 +783,7 @@ public IPromise Then(Action onResolved, Action onRejected) }; ActionHandlers(resultPromise, resolveHandler, rejectHandler); + ProgressHandlers(resultPromise, v => resultPromise.ReportProgress(v)); return resultPromise; } @@ -714,6 +808,17 @@ private void ActionHandlers(IRejectable resultPromise, Action resolveHandler, Ac } } + /// + /// Helper function to invoke or register progress handlers. + /// + private void ProgressHandlers(IRejectable resultPromise, Action progressHandler) + { + if (CurState == PromiseState.Pending) + { + AddProgressHandler(progressHandler, resultPromise); + } + } + /// /// Chain an enumerable of promises, all of which must resolve. /// The resulting promise is resolved when all of the promises have resolved. @@ -759,6 +864,7 @@ public static IPromise All(IEnumerable promises) var remainingCount = promisesArray.Length; var resultPromise = new Promise(); resultPromise.WithName("All"); + var progress = new float[remainingCount]; promisesArray.Each((promise, index) => { @@ -780,6 +886,11 @@ public static IPromise All(IEnumerable promises) resultPromise.Resolve(); } }) + .Progress(v => + { + progress[index] = v; + resultPromise.ReportProgress(progress.Average()); + }) .Done(); }); @@ -864,6 +975,8 @@ public static IPromise Race(IEnumerable promises) var resultPromise = new Promise(); resultPromise.WithName("Race"); + var progress = new float[promisesArray.Length]; + promisesArray.Each((promise, index) => { promise @@ -882,6 +995,11 @@ public static IPromise Race(IEnumerable promises) resultPromise.Resolve(); } }) + .Progress(v => + { + progress[index] = v; + resultPromise.ReportProgress(progress.Max()); + }) .Done(); }); @@ -952,6 +1070,52 @@ public IPromise Finally(Func> onCom return promise.Then(() => { return onComplete(); }); } + public IPromise Progress(Action onProgress) + { + var resultPromise = new Promise(); + resultPromise.WithName(Name); + + this.Then(() => { resultPromise.Resolve(); }); + this.Catch((e) => { resultPromise.Reject(e); }); + + Action progressHandler = v => + { + if (onProgress != null) + { + onProgress(v); + } + + resultPromise.ReportProgress(v); + }; + + ProgressHandlers(resultPromise, progressHandler); + + return resultPromise; + } + + public IPromise Progress(Func onProgress) + { + var resultPromise = new Promise(); + resultPromise.WithName(Name); + + this.Then(() => { resultPromise.Resolve(); }); + this.Catch((e) => { resultPromise.Reject(e); }); + + Action progressHandler = v => + { + if (onProgress != null) + { + v = onProgress(v); + } + + resultPromise.ReportProgress(v); + }; + + ProgressHandlers(resultPromise, progressHandler); + + return resultPromise; + } + /// /// Raises the UnhandledException event. /// From d8f43a7f636a44e6b7eee034c7cc0e5b3f35366e Mon Sep 17 00:00:00 2001 From: Alberto Alonso Date: Wed, 31 Jan 2018 00:05:27 +0200 Subject: [PATCH 41/80] Simplified Progress(Action onProgress). --- Promise.cs | 24 +++++------------------- Promise_NonGeneric.cs | 22 ++++------------------ 2 files changed, 9 insertions(+), 37 deletions(-) diff --git a/Promise.cs b/Promise.cs index 583083b..3fad5c0 100644 --- a/Promise.cs +++ b/Promise.cs @@ -973,27 +973,13 @@ public IPromise Finally(Func> onCom return promise.Then(onComplete); } - public IPromise Progress(Action onProgress) + public IPromise Progress(Action onProgress) { - var resultPromise = new Promise(); - resultPromise.WithName(Name); - - this.Then((x) => { resultPromise.Resolve(x); }); - this.Catch((e) => { resultPromise.Reject(e); }); - - Action progressHandler = v => + if (onProgress != null) { - if (onProgress != null) - { - onProgress(v); - } - - resultPromise.ReportProgress(v); - }; - - ProgressHandlers(resultPromise, progressHandler); - - return resultPromise; + ProgressHandlers(this, onProgress); + } + return this; } public IPromise Progress(Func onProgress) diff --git a/Promise_NonGeneric.cs b/Promise_NonGeneric.cs index 263fb50..f8e5495 100644 --- a/Promise_NonGeneric.cs +++ b/Promise_NonGeneric.cs @@ -1072,25 +1072,11 @@ public IPromise Finally(Func> onCom public IPromise Progress(Action onProgress) { - var resultPromise = new Promise(); - resultPromise.WithName(Name); - - this.Then(() => { resultPromise.Resolve(); }); - this.Catch((e) => { resultPromise.Reject(e); }); - - Action progressHandler = v => + if (onProgress != null) { - if (onProgress != null) - { - onProgress(v); - } - - resultPromise.ReportProgress(v); - }; - - ProgressHandlers(resultPromise, progressHandler); - - return resultPromise; + ProgressHandlers(this, onProgress); + } + return this; } public IPromise Progress(Func onProgress) From c6009b66210cf45e10e3ec13b6ba606ba5e0260e Mon Sep 17 00:00:00 2001 From: Alberto Alonso Date: Wed, 31 Jan 2018 00:07:00 +0200 Subject: [PATCH 42/80] Fixed compile error introduced in my previous commit. --- Promise.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Promise.cs b/Promise.cs index 3fad5c0..e4a2413 100644 --- a/Promise.cs +++ b/Promise.cs @@ -973,7 +973,7 @@ public IPromise Finally(Func> onCom return promise.Then(onComplete); } - public IPromise Progress(Action onProgress) + public IPromise Progress(Action onProgress) { if (onProgress != null) { From f38090493b46eeb344c025fa0064d15d734da0d0 Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Wed, 31 Jan 2018 15:54:18 +1000 Subject: [PATCH 43/80] Added contributing guide --- CONTRIBUTING.md | 50 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..a662d8f --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,50 @@ +# How to contribute to C-Sharp-Promise + +👍🎉 First off, thanks for taking the time to contribute! 🎉👍 + + +## Reporting bugs + +Any bug reports are useful, although there are a few things you can do that will +make it easier for maintainers and other users to understand your report, +reproduce the behaviour, and find related reports. + + - **Use a clear and descriptive title** for the issue to identify the problem. + - **Describe the exact steps which reproduce the problem** in as many details + as possible. Code examples or links to repos to reproduce the issue are + always helpful. + - **Describe the behaviour you observed after following the steps** and point + out what exactly the problem is with that behaviour. + - **Explain which behaviour you expected to see instead and why.** Since + C-Sharp-Promise is designed to replicate [JavaScript promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises), + code that works with JavaScript promises but gives a different result or + breaks with C-Sharp-Promise would be a a good example. + - **Check the existing open issues on GitHub** to see if someone else has had + the same problem as you. + +Make sure to file bugs as [GitHub issues](https://github.com/Real-Serious-Games/C-Sharp-Promise/issues), +since that way everyone working on the library can see it and potentially help. + + +## Pull requests + +Before we merge pull requests there are a few things we look for to make sure +the code is maintainable and up the same standard as the rest of the library. + + - Make sure you've written comprehensive unit tests for the feature, or + modify existing tests if the feature changes functionality. + - Check that your code conforms to the same style as existing code. Ensure that + your editor is set up to read the [.editorconfig](http://editorconfig.org/) + file for consistent spacing and line endings. We also try to keep our code + style consistent with the [Microsoft C# Coding Conventions](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/inside-a-program/coding-conventions) + and [Framework Design Guidelines](https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/index). + - Make sure that the [Travis CI build](https://travis-ci.org/Real-Serious-Games/C-Sharp-Promise) + succeeds. This should run automatically when you create a pull request, but + should have the same result as building the solution and running all the + tests locally. We will not accept any pull requests that fail to build or + contain failing tests. + - If you have added a new feature, add a section to README.md describing the + feature and how to use it. + +In addition, if your pull request breaks any existing functionality it's +unlikely we will merge it unless there is a very good reason. \ No newline at end of file From 70fd6da8f552a38065d66e01916a4fda749bc52b Mon Sep 17 00:00:00 2001 From: Alberto Alonso Date: Thu, 1 Feb 2018 00:02:06 +0200 Subject: [PATCH 44/80] Fixed the progress reporting. Added support for a third parameter in Then() to receive progress callbacks. --- Promise.cs | 115 +++++++++++++++++++++++++++--------------- Promise_NonGeneric.cs | 107 ++++++++++++++++++++++++--------------- 2 files changed, 141 insertions(+), 81 deletions(-) diff --git a/Promise.cs b/Promise.cs index e4a2413..bee41c9 100644 --- a/Promise.cs +++ b/Promise.cs @@ -86,6 +86,27 @@ Func> onRejected /// IPromise Then(Action onResolved, Action onRejected); + /// + /// Add a resolved callback, a rejected callback and a progress callback. + /// The resolved callback chains a value promise (optionally converting to a different value type). + /// + IPromise Then( + Func> onResolved, + Func> onRejected, + Action onProgress + ); + + /// + /// Add a resolved callback, a rejected callback and a progress callback. + /// The resolved callback chains a non-value promise. + /// + IPromise Then(Func onResolved, Action onRejected, Action onProgress); + + /// + /// Add a resolved callback, a rejected callback and a progress callback. + /// + IPromise Then(Action onResolved, Action onRejected, Action onProgress); + /// /// Return a new promise with a different value. /// May also change the type of the value. @@ -152,13 +173,6 @@ Func> onRejected /// of the promise. /// IPromise Progress(Action onProgress); - - /// - /// Add a progress function that can convert the progress value. - /// Progress callbacks will be called whenever the promise owner reports progress towards the resolution - /// of the promise. - /// - IPromise Progress(Func onProgress); } /// @@ -561,7 +575,7 @@ public IPromise Catch(Func onRejected) /// public IPromise Then(Func> onResolved) { - return Then(onResolved, null); + return Then(onResolved, null, null); } /// @@ -569,7 +583,7 @@ public IPromise Then(Func public IPromise Then(Func onResolved) { - return Then(onResolved, null); + return Then(onResolved, null, null); } /// @@ -577,7 +591,7 @@ public IPromise Then(Func onResolved) /// public IPromise Then(Action onResolved) { - return Then(onResolved, null); + return Then(onResolved, null, null); } /// @@ -585,9 +599,40 @@ public IPromise Then(Action onResolved) /// The resolved callback chains a value promise (optionally converting to a different value type). /// public IPromise Then( - Func> onResolved, + Func> onResolved, Func> onRejected ) + { + return Then(onResolved, onRejected, null); + } + + /// + /// Add a resolved callback and a rejected callback. + /// The resolved callback chains a non-value promise. + /// + public IPromise Then(Func onResolved, Action onRejected) + { + return Then(onResolved, onRejected, null); + } + + /// + /// Add a resolved callback and a rejected callback. + /// + public IPromise Then(Action onResolved, Action onRejected) + { + return Then(onResolved, onRejected, null); + } + + + /// + /// Add a resolved callback, a rejected callback and a progress callback. + /// The resolved callback chains a value promise (optionally converting to a different value type). + /// + public IPromise Then( + Func> onResolved, + Func> onRejected, + Action onProgress + ) { // This version of the function must supply an onResolved. // Otherwise there is now way to get the converted value to pass to the resulting promise. @@ -599,6 +644,7 @@ Func> onRejected Action resolveHandler = v => { onResolved(v) + .Progress(progress => resultPromise.ReportProgress(progress)) .Then( // Should not be necessary to specify the arg type on the next line, but Unity (mono) has an internal compiler error otherwise. (ConvertedT chainedValue) => resultPromise.Resolve(chainedValue), @@ -629,16 +675,19 @@ Func> onRejected }; ActionHandlers(resultPromise, resolveHandler, rejectHandler); - ProgressHandlers(resultPromise, v => resultPromise.ReportProgress(v)); + if (onProgress != null) + { + ProgressHandlers(this, onProgress); + } return resultPromise; } /// - /// Add a resolved callback and a rejected callback. + /// Add a resolved callback, a rejected callback and a progress callback. /// The resolved callback chains a non-value promise. /// - public IPromise Then(Func onResolved, Action onRejected) + public IPromise Then(Func onResolved, Action onRejected, Action onProgress) { var resultPromise = new Promise(); resultPromise.WithName(Name); @@ -648,6 +697,7 @@ public IPromise Then(Func onResolved, Action onR if (onResolved != null) { onResolved(v) + .Progress(progress => resultPromise.ReportProgress(progress)) .Then( () => resultPromise.Resolve(), ex => resultPromise.Reject(ex) @@ -670,15 +720,18 @@ public IPromise Then(Func onResolved, Action onR }; ActionHandlers(resultPromise, resolveHandler, rejectHandler); - ProgressHandlers(resultPromise, v => resultPromise.ReportProgress(v)); + if (onProgress != null) + { + ProgressHandlers(this, onProgress); + } return resultPromise; } /// - /// Add a resolved callback and a rejected callback. + /// Add a resolved callback, a rejected callback and a progress callback. /// - public IPromise Then(Action onResolved, Action onRejected) + public IPromise Then(Action onResolved, Action onRejected, Action onProgress) { var resultPromise = new Promise(); resultPromise.WithName(Name); @@ -704,7 +757,10 @@ public IPromise Then(Action onResolved, Action }; ActionHandlers(resultPromise, resolveHandler, rejectHandler); - ProgressHandlers(resultPromise, v => resultPromise.ReportProgress(v)); + if (onProgress != null) + { + ProgressHandlers(this, onProgress); + } return resultPromise; } @@ -981,28 +1037,5 @@ public IPromise Progress(Action onProgress) } return this; } - - public IPromise Progress(Func onProgress) - { - var resultPromise = new Promise(); - resultPromise.WithName(Name); - - this.Then((x) => { resultPromise.Resolve(x); }); - this.Catch((e) => { resultPromise.Reject(e); }); - - Action progressHandler = v => - { - if (onProgress != null) - { - v = onProgress(v); - } - - resultPromise.ReportProgress(v); - }; - - ProgressHandlers(resultPromise, progressHandler); - - return resultPromise; - } } } diff --git a/Promise_NonGeneric.cs b/Promise_NonGeneric.cs index f8e5495..114ce5a 100644 --- a/Promise_NonGeneric.cs +++ b/Promise_NonGeneric.cs @@ -78,6 +78,23 @@ public interface IPromise /// IPromise Then(Action onResolved, Action onRejected); + /// + /// Add a resolved callback, a rejected callback and a progress callback. + /// The resolved callback chains a value promise (optionally converting to a different value type). + /// + IPromise Then(Func> onResolved, Action onRejected, Action onProgress); + + /// + /// Add a resolved callback, a rejected callback and a progress callback. + /// The resolved callback chains a non-value promise. + /// + IPromise Then(Func onResolved, Action onRejected, Action onProgress); + + /// + /// Add a resolved callback, a rejected callback and a progress callback. + /// + IPromise Then(Action onResolved, Action onRejected, Action onProgress); + /// /// Chain an enumerable of promises, all of which must resolve. /// The resulting promise is resolved when all of the promises have resolved. @@ -143,13 +160,6 @@ public interface IPromise /// of the promise. /// IPromise Progress(Action onProgress); - - /// - /// Add a progress function that can convert the progress value. - /// Progress callbacks will be called whenever the promise owner reports progress towards the resolution - /// of the promise. - /// - IPromise Progress(Func onProgress); } /// @@ -655,7 +665,7 @@ public IPromise Catch(Action onRejected) /// public IPromise Then(Func> onResolved) { - return Then(onResolved, null); + return Then(onResolved, null, null); } /// @@ -663,7 +673,7 @@ public IPromise Then(Func> onResolv /// public IPromise Then(Func onResolved) { - return Then(onResolved, null); + return Then(onResolved, null, null); } /// @@ -671,7 +681,7 @@ public IPromise Then(Func onResolved) /// public IPromise Then(Action onResolved) { - return Then(onResolved, null); + return Then(onResolved, null, null); } /// @@ -679,6 +689,35 @@ public IPromise Then(Action onResolved) /// The resolved callback chains a value promise (optionally converting to a different value type). /// public IPromise Then(Func> onResolved, Action onRejected) + { + return Then(onResolved, onRejected, null); + } + + /// + /// Add a resolved callback and a rejected callback. + /// The resolved callback chains a non-value promise. + /// + public IPromise Then(Func onResolved, Action onRejected) + { + return Then(onResolved, onRejected, null); + } + + /// + /// Add a resolved callback and a rejected callback. + /// + public IPromise Then(Action onResolved, Action onRejected) + { + return Then(onResolved, onRejected, null); + } + + /// + /// Add a resolved callback, a rejected callback and a progress callback. + /// The resolved callback chains a value promise (optionally converting to a different value type). + /// + public IPromise Then( + Func> onResolved, + Action onRejected, + Action onProgress) { // This version of the function must supply an onResolved. // Otherwise there is now way to get the converted value to pass to the resulting promise. @@ -690,6 +729,7 @@ public IPromise Then(Func> onResolv Action resolveHandler = () => { onResolved() + .Progress(progress => resultPromise.ReportProgress(progress)) .Then( // Should not be necessary to specify the arg type on the next line, but Unity (mono) has an internal compiler error otherwise. (ConvertedT chainedValue) => resultPromise.Resolve(chainedValue), @@ -708,16 +748,19 @@ public IPromise Then(Func> onResolv }; ActionHandlers(resultPromise, resolveHandler, rejectHandler); - ProgressHandlers(resultPromise, v => resultPromise.ReportProgress(v)); + if (onProgress != null) + { + ProgressHandlers(this, onProgress); + } return resultPromise; } /// - /// Add a resolved callback and a rejected callback. + /// Add a resolved callback, a rejected callback and a progress callback. /// The resolved callback chains a non-value promise. /// - public IPromise Then(Func onResolved, Action onRejected) + public IPromise Then(Func onResolved, Action onRejected, Action onProgress) { var resultPromise = new Promise(); resultPromise.WithName(Name); @@ -727,6 +770,7 @@ public IPromise Then(Func onResolved, Action onRejected) if (onResolved != null) { onResolved() + .Progress(progress => resultPromise.ReportProgress(progress)) .Then( () => resultPromise.Resolve(), ex => resultPromise.Reject(ex) @@ -749,15 +793,18 @@ public IPromise Then(Func onResolved, Action onRejected) }; ActionHandlers(resultPromise, resolveHandler, rejectHandler); - ProgressHandlers(resultPromise, v => resultPromise.ReportProgress(v)); + if (onProgress != null) + { + ProgressHandlers(this, onProgress); + } return resultPromise; } /// - /// Add a resolved callback and a rejected callback. + /// Add a resolved callback, a rejected callback and a progress callback. /// - public IPromise Then(Action onResolved, Action onRejected) + public IPromise Then(Action onResolved, Action onRejected, Action onProgress) { var resultPromise = new Promise(); resultPromise.WithName(Name); @@ -783,7 +830,10 @@ public IPromise Then(Action onResolved, Action onRejected) }; ActionHandlers(resultPromise, resolveHandler, rejectHandler); - ProgressHandlers(resultPromise, v => resultPromise.ReportProgress(v)); + if (onProgress != null) + { + ProgressHandlers(this, onProgress); + } return resultPromise; } @@ -1079,29 +1129,6 @@ public IPromise Progress(Action onProgress) return this; } - public IPromise Progress(Func onProgress) - { - var resultPromise = new Promise(); - resultPromise.WithName(Name); - - this.Then(() => { resultPromise.Resolve(); }); - this.Catch((e) => { resultPromise.Reject(e); }); - - Action progressHandler = v => - { - if (onProgress != null) - { - v = onProgress(v); - } - - resultPromise.ReportProgress(v); - }; - - ProgressHandlers(resultPromise, progressHandler); - - return resultPromise; - } - /// /// Raises the UnhandledException event. /// From ed0a1d1db28b81408c01b078d79924a40a3220a1 Mon Sep 17 00:00:00 2001 From: Alberto Alonso Date: Thu, 1 Feb 2018 01:02:30 +0200 Subject: [PATCH 45/80] Added progress reporting test cases. --- Tests/Promise.Tests.csproj | 2 +- Tests/PromiseProgressTests.cs | 126 ++++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 Tests/PromiseProgressTests.cs diff --git a/Tests/Promise.Tests.csproj b/Tests/Promise.Tests.csproj index 8161139..07a4eb0 100644 --- a/Tests/Promise.Tests.csproj +++ b/Tests/Promise.Tests.csproj @@ -47,12 +47,12 @@ - False ..\packages\xunit.1.9.2\lib\net20\xunit.dll + diff --git a/Tests/PromiseProgressTests.cs b/Tests/PromiseProgressTests.cs new file mode 100644 index 0000000..14c211a --- /dev/null +++ b/Tests/PromiseProgressTests.cs @@ -0,0 +1,126 @@ +using System; +using Xunit; + +namespace RSG.Tests +{ + public class PromiseProgressTests + { + [Fact] + public void can_report_simple_progress() + { + var expectedStep = 0.25f; + var currentProgress = 0f; + var promise = new Promise(); + + promise.Progress(v => + { + Assert.InRange(expectedStep - (v - currentProgress), -Math.E, Math.E); + currentProgress = v; + }); + + for (float progress = 0.25f; progress < 1f; progress += 0.25f) + promise.ReportProgress(progress); + promise.ReportProgress(1f); + + Assert.Equal(1f, currentProgress); + } + + [Fact] + public void can_handle_onProgress() + { + var promise = new Promise(); + var progress = 0f; + + promise.Then(null, null, v => progress = v); + + promise.ReportProgress(1f); + + Assert.Equal(1f, progress); + } + + [Fact] + public void can_handle_chained_onProgress() + { + var promiseA = new Promise(); + var promiseB = new Promise(); + var progressA = 0f; + var progressB = 0f; + + promiseA + .Then(() => promiseB, null, v => progressA = v) + .Progress(v => progressB = v) + .Done(); + + promiseA.ReportProgress(1f); + promiseA.Resolve(); + promiseB.ReportProgress(2f); + promiseB.Resolve(); + + Assert.Equal(1f, progressA); + Assert.Equal(2f, progressB); + } + + [Fact] + public void can_do_weighted_average() + { + var promiseA = new Promise(); + var promiseB = new Promise(); + var promiseC = new Promise(); + + var expectedSteps = new float[] { 0.1f, 0.2f, 0.6f, 1f }; + var currentProgress = 0f; + int currentStep = 0; + + promiseC. + Progress(v => + { + Assert.InRange(currentStep, 0, expectedSteps.Length - 1); + Assert.Equal(v, expectedSteps[currentStep]); + currentProgress = v; + ++currentStep; + }) + ; + + promiseA. + Progress(v => promiseC.ReportProgress(v * 0.2f)) + .Then(() => promiseB) + .Progress(v => promiseC.ReportProgress(0.2f + 0.8f * v)) + .Then(() => promiseC.Resolve()) + .Catch(ex => promiseC.Reject(ex)) + ; + + promiseA.ReportProgress(0.5f); + promiseA.ReportProgress(1f); + promiseA.Resolve(); + promiseB.ReportProgress(0.5f); + promiseB.ReportProgress(1f); + promiseB.Resolve(); + + Assert.Equal(1f, currentProgress); + } + + + [Fact] + public void chain_multiple_promises_reporting_progress() + { + var promiseA = new Promise(); + var promiseB = new Promise(); + var progressA = 0f; + var progressB = 0f; + + promiseA + .Progress(v => progressA = v) + .Then(() => promiseB) + .Progress(v => progressB = v) + .Done(); + + promiseA.ReportProgress(1f); + promiseA.Resolve(); + promiseB.ReportProgress(2f); + promiseB.Resolve(); + + Assert.Equal(1f, progressA); + Assert.Equal(2f, progressB); + } + } +} From 1f18335113cb80f36e35efacabc52a0a5277d683 Mon Sep 17 00:00:00 2001 From: Alberto Alonso Date: Thu, 1 Feb 2018 01:16:41 +0200 Subject: [PATCH 46/80] Added some extra test cases for handling exceptions during invokation of ReportProgress on non-pending promises. --- Tests/PromiseProgressTests.cs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/Tests/PromiseProgressTests.cs b/Tests/PromiseProgressTests.cs index 14c211a..0667747 100644 --- a/Tests/PromiseProgressTests.cs +++ b/Tests/PromiseProgressTests.cs @@ -61,7 +61,7 @@ public void can_handle_chained_onProgress() } [Fact] - public void can_do_weighted_average() + public void can_do_progress_weighted_average() { var promiseA = new Promise(); var promiseB = new Promise(); @@ -122,5 +122,23 @@ public void chain_multiple_promises_reporting_progress() Assert.Equal(1f, progressA); Assert.Equal(2f, progressB); } + + [Fact] + public void exception_is_thrown_for_progress_after_resolve() + { + var promise = new Promise(); + promise.Resolve(); + + Assert.Throws(() => promise.ReportProgress(1f)); + } + + [Fact] + public void exception_is_thrown_for_progress_after_reject() + { + var promise = new Promise(); + promise.Reject(new ApplicationException()); + + Assert.Throws(() => promise.ReportProgress(1f)); + } } } From 36712fd3dd77df8fbef913e91738a2d010a696af Mon Sep 17 00:00:00 2001 From: Alberto Alonso Date: Thu, 1 Feb 2018 01:59:58 +0200 Subject: [PATCH 47/80] Added documentation for progress reporting. Added test cases for All and Race. --- README.md | 44 ++++++++++++++++++++++++++++++++- Tests/PromiseProgressTests.cs | 46 ++++++++++++++++++++++++++++++++++- 2 files changed, 88 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f26b80c..674ac8a 100644 --- a/README.md +++ b/README.md @@ -257,9 +257,33 @@ Then forward the exceptions to your own logging system: private void Promise_UnhandledException(object sender, ExceptionEventArgs e) { - Log.Error(e.Exception, "An unhandled proimses exception occured!"); + Log.Error(e.Exception, "An unhandled promises exception occured!"); } +## Progress reporting + +Promises can additionally report their progress towards completion, allowing the implementor to give the user feedback on the asynchronous operation. The general convention is to report progress as a value from 0 to 1. + +For this, you can either call `Progress` in the promise definition chain or add a third parameter to the `Then` method. + +Listening for progress reporting from a promise using `Progress`: + + var promise = new Promise(); + promise.Progress((progress) => Log.Info("Current progress is " + (100f * progress) + "%")); + +Listening for progress on a `Then` call: + + var promiseA = new Promise(); + var promiseB = new Promise(); + promise + .Then(() => promiseB, null, (progress) => Log.Info("promiseA made progress: " + progress)) + .Progress(progress => Log.Info("promiseB made progress: " + progress)); + +In order to report progress for a promise, you need to call the `ReportProgress` method: + + var promise = new Promise(); + promise.ReportProgress(0.5f); // Report a 50% completion + ## Promises that are already Resolved/Rejected For convenience or testing you will at some point need to create a promise that *starts out* in the resolved or rejected state. This is easy to achieve using *Resolved* and *Rejected* functions: @@ -302,6 +326,8 @@ Here is an example that extracts links from multiple pages and merges the result } }); +When listening for progress events in an All operation, the progress that you will receive will be the average of all the progress values reported by all the given promises. + ## Chaining Multiple Async Operations The *ThenAll* function is a convenient way of chaining multiple promise onto an existing promise: @@ -337,6 +363,8 @@ The *Race* and *ThenRace* functions are similar to the *All* and *ThenAll* funct .Done(result => ...); // The result has come from whichever of // the async operations completed first. +When listening for progress events in a race operation, the progress that you will receive will be the maximum of those reported by all the given promises. + ## Chaining Synchronous Actions that have no Result The *Then* function can be used to chain synchronous operations that yield no result. @@ -462,6 +490,20 @@ We can easily combine sequential and parallel operations to build very expressiv I'm starting to feel like we are defining behavior trees. +## Weighted averaging of progress on multiple promises + +If you have a promise that comprises a sequence of other promises, you might want to report the total progress for these, and even give more weight to the progress of some promise over another. In this example, we are first downloading an asset from some URL and then we are loading the downloaded asset into memory. We consider that the time it takes to download the asset will be an 80% of the total time, while the time to load it into memory is a 20%: + + var promise = new Promise(); + + Download(url) + .Progress((downloadProgress) => promise.ReportProgress(0.8f * downloadProgress)) + .Then((asset) => LoadAssetIntoMemory(asset)) + .Progress((loadProgress) => promise.ReportProgress(0.8f + 0.2f * loadProgress)) + .Then(() => promise.Resolve()) + .Catch((ex) => promise.Reject(ex)); + + return promise; ## PromiseTimer class diff --git a/Tests/PromiseProgressTests.cs b/Tests/PromiseProgressTests.cs index 0667747..f1fe447 100644 --- a/Tests/PromiseProgressTests.cs +++ b/Tests/PromiseProgressTests.cs @@ -75,7 +75,7 @@ public void can_do_progress_weighted_average() Progress(v => { Assert.InRange(currentStep, 0, expectedSteps.Length - 1); - Assert.Equal(v, expectedSteps[currentStep]); + Assert.InRange(v - expectedSteps[currentStep], -Math.E, Math.E); currentProgress = v; ++currentStep; }) @@ -140,5 +140,49 @@ public void exception_is_thrown_for_progress_after_reject() Assert.Throws(() => promise.ReportProgress(1f)); } + + [Fact] + public void all_progress_is_averaged() + { + var promiseA = new Promise(); + var promiseB = new Promise(); + var promiseC = new Promise(); + var promiseD = new Promise(); + + int currentProgress = 0; + var expectedProgress = new float[] { 0.25f, 0.50f, 0.75f, 1f }; + + Promise.All(new IPromise[] { promiseA, promiseB, promiseC, promiseD }) + .Progress(progress => + { + Assert.InRange(currentProgress, 0, expectedProgress.Length - 1); + Assert.InRange(progress - expectedProgress[currentProgress], -Math.E, Math.E); + }); + + promiseA.ReportProgress(1f); + promiseC.ReportProgress(1f); + promiseB.ReportProgress(1f); + promiseD.ReportProgress(1f); + } + + [Fact] + public void race_progress_is_maxed() + { + var promiseA = new Promise(); + var promiseB = new Promise(); + + Promise.Race(new IPromise[] { promiseA, promiseB }) + .Progress(progress => + { + Assert.Equal(progress, 0.5f); + }); + + promiseA.ReportProgress(0.5f); + promiseB.ReportProgress(0.1f); + promiseB.ReportProgress(0.2f); + promiseB.ReportProgress(0.3f); + promiseB.ReportProgress(0.4f); + promiseB.ReportProgress(0.5f); + } } } From 5421382c2596cebb4dabac61b36dafebdb7a15e5 Mon Sep 17 00:00:00 2001 From: Alberto Alonso Date: Fri, 2 Feb 2018 00:40:42 +0200 Subject: [PATCH 48/80] - Improved existing tests. - Added extra tests for generic promises. - Fixed some issues that actually arose thanks to doing good tests. --- Promise.cs | 20 +-- Promise_NonGeneric.cs | 20 +-- Tests/Promise.Tests.csproj | 1 + Tests/PromiseProgressTests.cs | 94 +++++++---- Tests/Promise_NonGeneric_ProgressTests.cs | 197 ++++++++++++++++++++++ 5 files changed, 275 insertions(+), 57 deletions(-) create mode 100644 Tests/Promise_NonGeneric_ProgressTests.cs diff --git a/Promise.cs b/Promise.cs index bee41c9..7cd71b3 100644 --- a/Promise.cs +++ b/Promise.cs @@ -858,6 +858,11 @@ public static IPromise> All(IEnumerable { promise + .Progress(v => + { + progress[index] = v; + resultPromise.ReportProgress(progress.Average()); + }) .Then(result => { results[index] = result; @@ -878,11 +883,6 @@ public static IPromise> All(IEnumerable - { - progress[index] = v; - resultPromise.ReportProgress(progress.Average()); - }) .Done(); }); @@ -939,6 +939,11 @@ public static IPromise Race(IEnumerable> promises promisesArray.Each((promise, index) => { promise + .Progress(v => + { + progress[index] = v; + resultPromise.ReportProgress(progress.Max()); + }) .Then(result => { if (resultPromise.CurState == PromiseState.Pending) @@ -954,11 +959,6 @@ public static IPromise Race(IEnumerable> promises resultPromise.Reject(ex); } }) - .Progress(v => - { - progress[index] = v; - resultPromise.ReportProgress(progress.Max()); - }) .Done(); }); diff --git a/Promise_NonGeneric.cs b/Promise_NonGeneric.cs index 114ce5a..506ad42 100644 --- a/Promise_NonGeneric.cs +++ b/Promise_NonGeneric.cs @@ -919,6 +919,11 @@ public static IPromise All(IEnumerable promises) promisesArray.Each((promise, index) => { promise + .Progress(v => + { + progress[index] = v; + resultPromise.ReportProgress(progress.Average()); + }) .Catch(ex => { if (resultPromise.CurState == PromiseState.Pending) @@ -936,11 +941,6 @@ public static IPromise All(IEnumerable promises) resultPromise.Resolve(); } }) - .Progress(v => - { - progress[index] = v; - resultPromise.ReportProgress(progress.Average()); - }) .Done(); }); @@ -1030,6 +1030,11 @@ public static IPromise Race(IEnumerable promises) promisesArray.Each((promise, index) => { promise + .Progress(v => + { + progress[index] = v; + resultPromise.ReportProgress(progress.Max()); + }) .Catch(ex => { if (resultPromise.CurState == PromiseState.Pending) @@ -1045,11 +1050,6 @@ public static IPromise Race(IEnumerable promises) resultPromise.Resolve(); } }) - .Progress(v => - { - progress[index] = v; - resultPromise.ReportProgress(progress.Max()); - }) .Done(); }); diff --git a/Tests/Promise.Tests.csproj b/Tests/Promise.Tests.csproj index 07a4eb0..3f45d9a 100644 --- a/Tests/Promise.Tests.csproj +++ b/Tests/Promise.Tests.csproj @@ -53,6 +53,7 @@ + diff --git a/Tests/PromiseProgressTests.cs b/Tests/PromiseProgressTests.cs index f1fe447..6677354 100644 --- a/Tests/PromiseProgressTests.cs +++ b/Tests/PromiseProgressTests.cs @@ -10,7 +10,7 @@ public void can_report_simple_progress() { var expectedStep = 0.25f; var currentProgress = 0f; - var promise = new Promise(); + var promise = new Promise(); promise.Progress(v => { @@ -28,7 +28,7 @@ public void can_report_simple_progress() [Fact] public void can_handle_onProgress() { - var promise = new Promise(); + var promise = new Promise(); var progress = 0f; promise.Then(null, null, v => progress = v); @@ -41,93 +41,105 @@ public void can_handle_onProgress() [Fact] public void can_handle_chained_onProgress() { - var promiseA = new Promise(); - var promiseB = new Promise(); + var promiseA = new Promise(); + var promiseB = new Promise(); var progressA = 0f; var progressB = 0f; + int result = 0; promiseA - .Then(() => promiseB, null, v => progressA = v) + .Then(v => promiseB, null, v => progressA = v) .Progress(v => progressB = v) + .Then(v => result = v) .Done(); promiseA.ReportProgress(1f); - promiseA.Resolve(); + promiseA.Resolve(-17); promiseB.ReportProgress(2f); - promiseB.Resolve(); + promiseB.Resolve(17); Assert.Equal(1f, progressA); Assert.Equal(2f, progressB); + Assert.Equal(17, result); } [Fact] public void can_do_progress_weighted_average() { - var promiseA = new Promise(); - var promiseB = new Promise(); - var promiseC = new Promise(); + var promiseA = new Promise(); + var promiseB = new Promise(); + var promiseC = new Promise(); - var expectedSteps = new float[] { 0.1f, 0.2f, 0.6f, 1f }; + var expectedProgress = new float[] { 0.1f, 0.2f, 0.6f, 1f }; var currentProgress = 0f; int currentStep = 0; + int result = 0; promiseC. Progress(v => { - Assert.InRange(currentStep, 0, expectedSteps.Length - 1); - Assert.InRange(v - expectedSteps[currentStep], -Math.E, Math.E); + Assert.InRange(currentStep, 0, expectedProgress.Length - 1); + Assert.Equal(v, expectedProgress[currentStep]); currentProgress = v; ++currentStep; }) + .Then(v => result = v) + .Done() ; promiseA. Progress(v => promiseC.ReportProgress(v * 0.2f)) - .Then(() => promiseB) + .Then(v => promiseB, null) .Progress(v => promiseC.ReportProgress(0.2f + 0.8f * v)) - .Then(() => promiseC.Resolve()) + .Then(v => promiseC.Resolve(v)) .Catch(ex => promiseC.Reject(ex)) ; promiseA.ReportProgress(0.5f); promiseA.ReportProgress(1f); - promiseA.Resolve(); + promiseA.Resolve(-17); promiseB.ReportProgress(0.5f); promiseB.ReportProgress(1f); - promiseB.Resolve(); + promiseB.Resolve(17); + Assert.Equal(expectedProgress.Length, currentStep); Assert.Equal(1f, currentProgress); + Assert.Equal(17, result); } [Fact] public void chain_multiple_promises_reporting_progress() { - var promiseA = new Promise(); - var promiseB = new Promise(); + var promiseA = new Promise(); + var promiseB = new Promise(); var progressA = 0f; var progressB = 0f; + int result = 0; promiseA .Progress(v => progressA = v) - .Then(() => promiseB) + .Then(v => promiseB, null) .Progress(v => progressB = v) - .Done(); + .Then(v => result = v) + .Done() + ; promiseA.ReportProgress(1f); - promiseA.Resolve(); + promiseA.Resolve(-17); promiseB.ReportProgress(2f); - promiseB.Resolve(); + promiseB.Resolve(17); Assert.Equal(1f, progressA); Assert.Equal(2f, progressB); + Assert.Equal(17, result); } [Fact] public void exception_is_thrown_for_progress_after_resolve() { - var promise = new Promise(); - promise.Resolve(); + var promise = new Promise(); + promise.Resolve(17); Assert.Throws(() => promise.ReportProgress(1f)); } @@ -135,7 +147,7 @@ public void exception_is_thrown_for_progress_after_resolve() [Fact] public void exception_is_thrown_for_progress_after_reject() { - var promise = new Promise(); + var promise = new Promise(); promise.Reject(new ApplicationException()); Assert.Throws(() => promise.ReportProgress(1f)); @@ -144,37 +156,43 @@ public void exception_is_thrown_for_progress_after_reject() [Fact] public void all_progress_is_averaged() { - var promiseA = new Promise(); - var promiseB = new Promise(); - var promiseC = new Promise(); - var promiseD = new Promise(); + var promiseA = new Promise(); + var promiseB = new Promise(); + var promiseC = new Promise(); + var promiseD = new Promise(); - int currentProgress = 0; + int currentStep = 0; var expectedProgress = new float[] { 0.25f, 0.50f, 0.75f, 1f }; - Promise.All(new IPromise[] { promiseA, promiseB, promiseC, promiseD }) + Promise.All(new IPromise[] { promiseA, promiseB, promiseC, promiseD }) .Progress(progress => { - Assert.InRange(currentProgress, 0, expectedProgress.Length - 1); - Assert.InRange(progress - expectedProgress[currentProgress], -Math.E, Math.E); + Assert.InRange(currentStep, 0, expectedProgress.Length - 1); + Assert.Equal(expectedProgress[currentStep], progress); + ++currentStep; }); promiseA.ReportProgress(1f); promiseC.ReportProgress(1f); promiseB.ReportProgress(1f); promiseD.ReportProgress(1f); + + Assert.Equal(expectedProgress.Length, currentStep); + Assert.Equal(expectedProgress.Length, currentStep); } [Fact] public void race_progress_is_maxed() { - var promiseA = new Promise(); - var promiseB = new Promise(); + var promiseA = new Promise(); + var promiseB = new Promise(); + int reportCount = 0; - Promise.Race(new IPromise[] { promiseA, promiseB }) + Promise.Race(new IPromise[] { promiseA, promiseB }) .Progress(progress => { Assert.Equal(progress, 0.5f); + ++reportCount; }); promiseA.ReportProgress(0.5f); @@ -183,6 +201,8 @@ public void race_progress_is_maxed() promiseB.ReportProgress(0.3f); promiseB.ReportProgress(0.4f); promiseB.ReportProgress(0.5f); + + Assert.Equal(6, reportCount); } } } diff --git a/Tests/Promise_NonGeneric_ProgressTests.cs b/Tests/Promise_NonGeneric_ProgressTests.cs new file mode 100644 index 0000000..078193a --- /dev/null +++ b/Tests/Promise_NonGeneric_ProgressTests.cs @@ -0,0 +1,197 @@ +using System; +using Xunit; + +namespace RSG.Tests +{ + public class Promise_NonGeneric_ProgressTests + { + [Fact] + public void can_report_simple_progress() + { + var expectedStep = 0.25f; + var currentProgress = 0f; + var promise = new Promise(); + + promise.Progress(v => + { + Assert.InRange(expectedStep - (v - currentProgress), -Math.E, Math.E); + currentProgress = v; + }); + + for (float progress = 0.25f; progress < 1f; progress += 0.25f) + promise.ReportProgress(progress); + promise.ReportProgress(1f); + + Assert.Equal(1f, currentProgress); + } + + [Fact] + public void can_handle_onProgress() + { + var promise = new Promise(); + var progress = 0f; + + promise.Then(null, null, v => progress = v); + + promise.ReportProgress(1f); + + Assert.Equal(1f, progress); + } + + [Fact] + public void can_handle_chained_onProgress() + { + var promiseA = new Promise(); + var promiseB = new Promise(); + var progressA = 0f; + var progressB = 0f; + + promiseA + .Then(() => promiseB, null, v => progressA = v) + .Progress(v => progressB = v) + .Done(); + + promiseA.ReportProgress(1f); + promiseA.Resolve(); + promiseB.ReportProgress(2f); + promiseB.Resolve(); + + Assert.Equal(1f, progressA); + Assert.Equal(2f, progressB); + } + + [Fact] + public void can_do_progress_weighted_average() + { + var promiseA = new Promise(); + var promiseB = new Promise(); + var promiseC = new Promise(); + + var expectedProgress = new float[] { 0.1f, 0.2f, 0.6f, 1f }; + var currentProgress = 0f; + int currentStep = 0; + + promiseC. + Progress(v => + { + Assert.InRange(currentStep, 0, expectedProgress.Length - 1); + Assert.Equal(v, expectedProgress[currentStep]); + currentProgress = v; + ++currentStep; + }) + ; + + promiseA. + Progress(v => promiseC.ReportProgress(v * 0.2f)) + .Then(() => promiseB) + .Progress(v => promiseC.ReportProgress(0.2f + 0.8f * v)) + .Then(() => promiseC.Resolve()) + .Catch(ex => promiseC.Reject(ex)) + ; + + promiseA.ReportProgress(0.5f); + promiseA.ReportProgress(1f); + promiseA.Resolve(); + promiseB.ReportProgress(0.5f); + promiseB.ReportProgress(1f); + promiseB.Resolve(); + + Assert.Equal(expectedProgress.Length, currentStep); + Assert.Equal(1f, currentProgress); + } + + + [Fact] + public void chain_multiple_promises_reporting_progress() + { + var promiseA = new Promise(); + var promiseB = new Promise(); + var progressA = 0f; + var progressB = 0f; + + promiseA + .Progress(v => progressA = v) + .Then(() => promiseB) + .Progress(v => progressB = v) + .Done(); + + promiseA.ReportProgress(1f); + promiseA.Resolve(); + promiseB.ReportProgress(2f); + promiseB.Resolve(); + + Assert.Equal(1f, progressA); + Assert.Equal(2f, progressB); + } + + [Fact] + public void exception_is_thrown_for_progress_after_resolve() + { + var promise = new Promise(); + promise.Resolve(); + + Assert.Throws(() => promise.ReportProgress(1f)); + } + + [Fact] + public void exception_is_thrown_for_progress_after_reject() + { + var promise = new Promise(); + promise.Reject(new ApplicationException()); + + Assert.Throws(() => promise.ReportProgress(1f)); + } + + [Fact] + public void all_progress_is_averaged() + { + var promiseA = new Promise(); + var promiseB = new Promise(); + var promiseC = new Promise(); + var promiseD = new Promise(); + + int currentStep = 0; + var expectedProgress = new float[] { 0.25f, 0.50f, 0.75f, 1f }; + + Promise.All(new IPromise[] { promiseA, promiseB, promiseC, promiseD }) + .Progress(progress => + { + Assert.InRange(currentStep, 0, expectedProgress.Length - 1); + Assert.Equal(expectedProgress[currentStep], progress); + ++currentStep; + }); + + promiseA.ReportProgress(1f); + promiseC.ReportProgress(1f); + promiseB.ReportProgress(1f); + promiseD.ReportProgress(1f); + + Assert.Equal(expectedProgress.Length, currentStep); + Assert.Equal(expectedProgress.Length, currentStep); + } + + [Fact] + public void race_progress_is_maxed() + { + var promiseA = new Promise(); + var promiseB = new Promise(); + int reportCount = 0; + + Promise.Race(new IPromise[] { promiseA, promiseB }) + .Progress(progress => + { + Assert.Equal(progress, 0.5f); + ++reportCount; + }); + + promiseA.ReportProgress(0.5f); + promiseB.ReportProgress(0.1f); + promiseB.ReportProgress(0.2f); + promiseB.ReportProgress(0.3f); + promiseB.ReportProgress(0.4f); + promiseB.ReportProgress(0.5f); + + Assert.Equal(6, reportCount); + } + } +} From d90eb88415d3583dee3145161dd1c2dc93184656 Mon Sep 17 00:00:00 2001 From: Alberto Alonso Date: Sun, 4 Feb 2018 05:47:05 +0200 Subject: [PATCH 49/80] Make resolved promises in .All() report a progress of 1. --- Promise.cs | 3 ++- Promise_NonGeneric.cs | 6 ++++-- Tests/PromiseProgressTests.cs | 21 ++++++++++++++++++++- Tests/Promise_NonGeneric_ProgressTests.cs | 21 ++++++++++++++++++++- 4 files changed, 46 insertions(+), 5 deletions(-) diff --git a/Promise.cs b/Promise.cs index 7cd71b3..56f1e55 100644 --- a/Promise.cs +++ b/Promise.cs @@ -1,4 +1,4 @@ -using RSG.Promises; +using RSG.Promises; using System; using System.Collections.Generic; using System.Linq; @@ -865,6 +865,7 @@ public static IPromise> All(IEnumerable { + progress[index] = 1f; results[index] = result; --remainingCount; diff --git a/Promise_NonGeneric.cs b/Promise_NonGeneric.cs index 506ad42..c6ae6b5 100644 --- a/Promise_NonGeneric.cs +++ b/Promise_NonGeneric.cs @@ -1,4 +1,4 @@ -using RSG.Promises; +using RSG.Promises; using System; using System.Collections.Generic; using System.Linq; @@ -934,6 +934,8 @@ public static IPromise All(IEnumerable promises) }) .Then(() => { + progress[index] = 1f; + --remainingCount; if (remainingCount <= 0) { @@ -1140,4 +1142,4 @@ internal static void PropagateUnhandledException(object sender, Exception ex) } } } -} \ No newline at end of file +} diff --git a/Tests/PromiseProgressTests.cs b/Tests/PromiseProgressTests.cs index 6677354..edd545e 100644 --- a/Tests/PromiseProgressTests.cs +++ b/Tests/PromiseProgressTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using Xunit; namespace RSG.Tests @@ -204,5 +204,24 @@ public void race_progress_is_maxed() Assert.Equal(6, reportCount); } + + [Fact] + public void all_progress_with_resolved() + { + var promiseA = new Promise(); + var promiseB = Promise.Resolved(17); + int reportedCount = 0; + + Promise.All(new IPromise[] { promiseA, promiseB }) + .Progress(progress => + { + ++reportedCount; + Assert.Equal(0.75f, progress); + }); + + promiseA.ReportProgress(0.5f); + + Assert.Equal(1, reportedCount); + } } } diff --git a/Tests/Promise_NonGeneric_ProgressTests.cs b/Tests/Promise_NonGeneric_ProgressTests.cs index 078193a..3b3962b 100644 --- a/Tests/Promise_NonGeneric_ProgressTests.cs +++ b/Tests/Promise_NonGeneric_ProgressTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using Xunit; namespace RSG.Tests @@ -193,5 +193,24 @@ public void race_progress_is_maxed() Assert.Equal(6, reportCount); } + + [Fact] + public void all_progress_with_resolved() + { + var promiseA = new Promise(); + var promiseB = Promise.Resolved(); + int reportedCount = 0; + + Promise.All(new IPromise[] { promiseA, promiseB }) + .Progress(progress => + { + ++reportedCount; + Assert.Equal(0.75f, progress); + }); + + promiseA.ReportProgress(0.5f); + + Assert.Equal(1, reportedCount); + } } } From bbaea8da9cf6c8d73903836ddba7731085b743c6 Mon Sep 17 00:00:00 2001 From: Alberto Alonso Date: Sun, 4 Feb 2018 06:26:54 +0200 Subject: [PATCH 50/80] Added ReportProgress to IPendingPromise. --- Promise.cs | 5 +++++ Promise_NonGeneric.cs | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/Promise.cs b/Promise.cs index 56f1e55..04ba804 100644 --- a/Promise.cs +++ b/Promise.cs @@ -200,6 +200,11 @@ public interface IPendingPromise : IRejectable /// Resolve the promise with a particular value. /// void Resolve(PromisedT value); + + /// + /// Report progress in a promise. + /// + void ReportProgress(float progress); } /// diff --git a/Promise_NonGeneric.cs b/Promise_NonGeneric.cs index c6ae6b5..60e5505 100644 --- a/Promise_NonGeneric.cs +++ b/Promise_NonGeneric.cs @@ -176,6 +176,11 @@ public interface IPendingPromise : IRejectable /// Resolve the promise with a particular value. /// void Resolve(); + + /// + /// Report progress in a promise. + /// + void ReportProgress(float progress); } /// From 1a7e0f4a89c9840f395af733d985c982d561cb33 Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Mon, 12 Feb 2018 10:04:37 +1000 Subject: [PATCH 51/80] Removed return default(PromisedT) from Promise.All --- Promise.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Promise.cs b/Promise.cs index 04ba804..462a9b0 100644 --- a/Promise.cs +++ b/Promise.cs @@ -887,7 +887,6 @@ public static IPromise> All(IEnumerable Date: Mon, 12 Feb 2018 10:16:30 +1000 Subject: [PATCH 52/80] Set project to C# 3 to ensure all new code is compatible with Unity For some reason auto readonly properties work in Unity even though the feature wasn't added till C# 6? Many other features of later C# versions do not though, so I set the whole project to an older version to make sure that anything that could potentially cause an issue throws a compiler error. --- C-Sharp-Promise.csproj | 1 + Promise.cs | 8 +++++--- Promise_NonGeneric.cs | 8 +++++--- Tuple.cs | 20 ++++++++++---------- 4 files changed, 21 insertions(+), 16 deletions(-) diff --git a/C-Sharp-Promise.csproj b/C-Sharp-Promise.csproj index 0de980b..74594bc 100644 --- a/C-Sharp-Promise.csproj +++ b/C-Sharp-Promise.csproj @@ -24,6 +24,7 @@ prompt 4 false + 3 C-Sharp-Promise.snk diff --git a/Promise.cs b/Promise.cs index 462a9b0..d2e2297 100644 --- a/Promise.cs +++ b/Promise.cs @@ -252,7 +252,9 @@ public class Promise : IPromise, IPendingPromise /// ID of the promise, useful for debugging. /// - public int Id { get; } + public int Id { get { return id; } } + + private readonly int id; /// /// Name of the promise, when set, useful for debugging. @@ -267,7 +269,7 @@ public class Promise : IPromise, IPendingPromise, Action> resolver) { this.CurState = PromiseState.Pending; - this.Id = Promise.NextId(); + this.id = Promise.NextId(); if (Promise.EnablePromiseTracking) { diff --git a/Promise_NonGeneric.cs b/Promise_NonGeneric.cs index 60e5505..8280d9c 100644 --- a/Promise_NonGeneric.cs +++ b/Promise_NonGeneric.cs @@ -327,7 +327,9 @@ public struct ResolveHandler /// /// ID of the promise, useful for debugging. /// - public int Id { get; } + public int Id { get { return id; } } + + private readonly int id; /// /// Name of the promise, when set, useful for debugging. @@ -342,7 +344,7 @@ public struct ResolveHandler public Promise() { this.CurState = PromiseState.Pending; - this.Id = NextId(); + this.id = NextId(); if (EnablePromiseTracking) { pendingPromises.Add(this); @@ -352,7 +354,7 @@ public Promise() public Promise(Action> resolver) { this.CurState = PromiseState.Pending; - this.Id = NextId(); + this.id = NextId(); if (EnablePromiseTracking) { pendingPromises.Add(this); diff --git a/Tuple.cs b/Tuple.cs index 2a48226..1859177 100644 --- a/Tuple.cs +++ b/Tuple.cs @@ -1,4 +1,4 @@ -namespace RSG +namespace RSG { /// /// Provides static methods for creating tuple objects. @@ -69,12 +69,12 @@ internal Tuple(T1 item1, T2 item2) /// /// Gets the value of the current tuple's first component. /// - public T1 Item1 { get; } + public T1 Item1 { get; private set; } /// /// Gets the value of the current tuple's second component. /// - public T2 Item2 { get; } + public T2 Item2 { get; private set; } } /// @@ -95,17 +95,17 @@ internal Tuple(T1 item1, T2 item2, T3 item3) /// /// Gets the value of the current tuple's first component. /// - public T1 Item1 { get; } + public T1 Item1 { get; private set; } /// /// Gets the value of the current tuple's second component. /// - public T2 Item2 { get; } + public T2 Item2 { get; private set; } /// /// Gets the value of the current tuple's third component. /// - public T3 Item3 { get; } + public T3 Item3 { get; private set; } } /// @@ -128,21 +128,21 @@ internal Tuple(T1 item1, T2 item2, T3 item3, T4 item4) /// /// Gets the value of the current tuple's first component. /// - public T1 Item1 { get; } + public T1 Item1 { get; private set; } /// /// Gets the value of the current tuple's second component. /// - public T2 Item2 { get; } + public T2 Item2 { get; private set; } /// /// Gets the value of the current tuple's third component. /// - public T3 Item3 { get; } + public T3 Item3 { get; private set; } /// /// Gets the value of the current tuple's fourth component. /// - public T4 Item4 { get; } + public T4 Item4 { get; private set; } } } From 53041a2aac23363610d4bd13cf5833ac5919176a Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Mon, 12 Feb 2018 10:48:14 +1000 Subject: [PATCH 53/80] Various minor code cleanups, fixed test that was missing assert --- Promise.cs | 43 +++----- PromiseHelpers.cs | 5 - PromiseTimer.cs | 2 +- Promise_NonGeneric.cs | 67 +++++------ Tests/PromiseProgressTests.cs | 14 +-- Tests/PromiseTests.cs | 118 ++++++-------------- Tests/PromiseTimerTests.cs | 16 +-- Tests/Promise_NonGeneric_ProgressTests.cs | 14 +-- Tests/Promise_NonGeneric_Tests.cs | 128 ++++++---------------- 9 files changed, 135 insertions(+), 272 deletions(-) diff --git a/Promise.cs b/Promise.cs index d2e2297..403bcaa 100644 --- a/Promise.cs +++ b/Promise.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; namespace RSG { @@ -273,7 +272,7 @@ public Promise() if (Promise.EnablePromiseTracking) { - Promise.pendingPromises.Add(this); + Promise.PendingPromises.Add(this); } } @@ -284,18 +283,12 @@ public Promise(Action, Action> resolver) if (Promise.EnablePromiseTracking) { - Promise.pendingPromises.Add(this); + Promise.PendingPromises.Add(this); } try { - resolver( - // Resolve - value => Resolve(value), - - // Reject - ex => Reject(ex) - ); + resolver(Resolve, Reject); } catch (Exception ex) { @@ -313,7 +306,7 @@ private void AddRejectHandler(Action onRejected, IRejectable rejectab rejectHandlers = new List(); } - rejectHandlers.Add(new RejectHandler() { callback = onRejected, rejectable = rejectable }); ; + rejectHandlers.Add(new RejectHandler { callback = onRejected, rejectable = rejectable }); } /// @@ -345,7 +338,7 @@ private void AddProgressHandler(Action onProgress, IRejectable rejectable progressHandlers = new List(); } - progressHandlers.Add(new ProgressHandler() { callback = onProgress, rejectable = rejectable }); + progressHandlers.Add(new ProgressHandler { callback = onProgress, rejectable = rejectable }); } /// @@ -435,7 +428,7 @@ public void Reject(Exception ex) if (Promise.EnablePromiseTracking) { - Promise.pendingPromises.Remove(this); + Promise.PendingPromises.Remove(this); } InvokeRejectHandlers(ex); @@ -456,7 +449,7 @@ public void Resolve(PromisedT value) if (Promise.EnablePromiseTracking) { - Promise.pendingPromises.Remove(this); + Promise.PendingPromises.Remove(this); } InvokeResolveHandlers(value); @@ -654,7 +647,7 @@ Action onProgress .Progress(progress => resultPromise.ReportProgress(progress)) .Then( // Should not be necessary to specify the arg type on the next line, but Unity (mono) has an internal compiler error otherwise. - (ConvertedT chainedValue) => resultPromise.Resolve(chainedValue), + chainedValue => resultPromise.Resolve(chainedValue), ex => resultPromise.Reject(ex) ); }; @@ -671,7 +664,7 @@ Action onProgress { onRejected(ex) .Then( - (ConvertedT chainedValue) => resultPromise.Resolve(chainedValue), + chainedValue => resultPromise.Resolve(chainedValue), callbackEx => resultPromise.Reject(callbackEx) ); } @@ -996,11 +989,11 @@ public static IPromise Rejected(Exception ex) public IPromise Finally(Action onComplete) { - Promise promise = new Promise(); + var promise = new Promise(); promise.WithName(Name); - this.Then((x) => { promise.Resolve(x); }); - this.Catch((e) => { + this.Then(x => promise.Resolve(x)); + this.Catch(e => { try { onComplete(); promise.Reject(e); @@ -1015,11 +1008,11 @@ public IPromise Finally(Action onComplete) [Obsolete("Use new version of Finally that is TC39 compliant, or Catch and Then.")] public IPromise Finally(Func onComplete) { - Promise promise = new Promise(); + var promise = new Promise(); promise.WithName(Name); - this.Then((x) => { promise.Resolve(); }); - this.Catch((e) => { promise.Resolve(); }); + this.Then(x => promise.Resolve()); + this.Catch(e => promise.Resolve()); return promise.Then(onComplete); } @@ -1027,11 +1020,11 @@ public IPromise Finally(Func onComplete) [Obsolete("Use new version of Finally that is TC39 compliant, or Catch and Then.")] public IPromise Finally(Func> onComplete) { - Promise promise = new Promise(); + var promise = new Promise(); promise.WithName(Name); - this.Then((x) => { promise.Resolve(); }); - this.Catch((e) => { promise.Resolve(); }); + this.Then(x => promise.Resolve()); + this.Catch(e => promise.Resolve()); return promise.Then(onComplete); } diff --git a/PromiseHelpers.cs b/PromiseHelpers.cs index 5d4ec6c..86d90b8 100644 --- a/PromiseHelpers.cs +++ b/PromiseHelpers.cs @@ -1,8 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - namespace RSG { public static class PromiseHelpers diff --git a/PromiseTimer.cs b/PromiseTimer.cs index 67d4fce..975b2c1 100644 --- a/PromiseTimer.cs +++ b/PromiseTimer.cs @@ -119,7 +119,7 @@ public class PromiseTimer : IPromiseTimer /// /// Currently pending promises /// - private LinkedList waiting = new LinkedList(); + private readonly LinkedList waiting = new LinkedList(); /// /// Resolve the returned promise once the time has elapsed diff --git a/Promise_NonGeneric.cs b/Promise_NonGeneric.cs index 8280d9c..ca35ab1 100644 --- a/Promise_NonGeneric.cs +++ b/Promise_NonGeneric.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; namespace RSG { @@ -272,12 +271,13 @@ public static event EventHandler UnhandledException /// /// Id for the next promise that is created. /// - private static int nextPromiseId = 0; + private static int nextPromiseId; /// /// Information about pending promises. /// - internal static HashSet pendingPromises = new HashSet(); + internal static readonly HashSet PendingPromises = + new HashSet(); /// /// Information about pending promises, useful for debugging. @@ -285,7 +285,7 @@ public static event EventHandler UnhandledException /// public static IEnumerable GetPendingPromises() { - return pendingPromises; + return PendingPromises; } /// @@ -347,7 +347,7 @@ public Promise() this.id = NextId(); if (EnablePromiseTracking) { - pendingPromises.Add(this); + PendingPromises.Add(this); } } @@ -357,18 +357,12 @@ public Promise(Action> resolver) this.id = NextId(); if (EnablePromiseTracking) { - pendingPromises.Add(this); + PendingPromises.Add(this); } try { - resolver( - // Resolve - () => Resolve(), - - // Reject - ex => Reject(ex) - ); + resolver(Resolve, Reject); } catch (Exception ex) { @@ -394,7 +388,7 @@ private void AddRejectHandler(Action onRejected, IRejectable rejectab rejectHandlers = new List(); } - rejectHandlers.Add(new RejectHandler() + rejectHandlers.Add(new RejectHandler { callback = onRejected, rejectable = rejectable @@ -411,7 +405,7 @@ private void AddResolveHandler(Action onResolved, IRejectable rejectable) resolveHandlers = new List(); } - resolveHandlers.Add(new ResolveHandler() + resolveHandlers.Add(new ResolveHandler { callback = onResolved, rejectable = rejectable @@ -428,7 +422,7 @@ private void AddProgressHandler(Action onProgress, IRejectable rejectable progressHandlers = new List(); } - progressHandlers.Add(new ProgressHandler() { callback = onProgress, rejectable = rejectable }); + progressHandlers.Add(new ProgressHandler { callback = onProgress, rejectable = rejectable }); } /// @@ -551,7 +545,7 @@ public void Reject(Exception ex) if (EnablePromiseTracking) { - pendingPromises.Remove(this); + PendingPromises.Remove(this); } InvokeRejectHandlers(ex); @@ -572,7 +566,7 @@ public void Resolve() if (EnablePromiseTracking) { - pendingPromises.Remove(this); + PendingPromises.Remove(this); } InvokeResolveHandlers(); @@ -602,7 +596,7 @@ public void Done(Action onResolved, Action onRejected) { Then(onResolved, onRejected) .Catch(ex => - Promise.PropagateUnhandledException(this, ex) + PropagateUnhandledException(this, ex) ); } @@ -615,7 +609,7 @@ public void Done(Action onResolved) { Then(onResolved) .Catch(ex => - Promise.PropagateUnhandledException(this, ex) + PropagateUnhandledException(this, ex) ); } @@ -739,7 +733,7 @@ public IPromise Then( .Progress(progress => resultPromise.ReportProgress(progress)) .Then( // Should not be necessary to specify the arg type on the next line, but Unity (mono) has an internal compiler error otherwise. - (ConvertedT chainedValue) => resultPromise.Resolve(chainedValue), + chainedValue => resultPromise.Resolve(chainedValue), ex => resultPromise.Reject(ex) ); }; @@ -883,7 +877,7 @@ private void ProgressHandlers(IRejectable resultPromise, Action progressH /// public IPromise ThenAll(Func> chain) { - return Then(() => Promise.All(chain())); + return Then(() => All(chain())); } /// @@ -915,7 +909,7 @@ public static IPromise All(IEnumerable promises) var promisesArray = promises.ToArray(); if (promisesArray.Length == 0) { - return Promise.Resolved(); + return Resolved(); } var remainingCount = promisesArray.Length; @@ -984,10 +978,7 @@ public static IPromise Sequence(IEnumerable> fns) { return fns.Aggregate( Resolved(), - (prevPromise, fn) => - { - return prevPromise.Then(() => fn()); - } + (prevPromise, fn) => prevPromise.Then(fn) ); } @@ -1089,11 +1080,11 @@ public static IPromise Rejected(Exception ex) public IPromise Finally(Action onComplete) { - Promise promise = new Promise(); + var promise = new Promise(); promise.WithName(Name); - this.Then(() => { promise.Resolve(); }); - this.Catch((e) => { + this.Then(() => promise.Resolve()); + this.Catch(e => { try { onComplete(); promise.Reject(e); @@ -1102,17 +1093,17 @@ public IPromise Finally(Action onComplete) } }); - return promise.Then(() => onComplete()); + return promise.Then(onComplete); } [Obsolete("Use new version of Finally that is TC39 compliant, or Catch and Then.")] public IPromise Finally(Func onComplete) { - Promise promise = new Promise(); + var promise = new Promise(); promise.WithName(Name); - this.Then(() => { promise.Resolve(); }); - this.Catch((e) => { promise.Resolve(); }); + this.Then(() => promise.Resolve()); + this.Catch(e => promise.Resolve()); return promise.Then(onComplete); } @@ -1120,13 +1111,13 @@ public IPromise Finally(Func onComplete) [Obsolete("Use new version of Finally that is TC39 compliant, or Catch and Then.")] public IPromise Finally(Func> onComplete) { - Promise promise = new Promise(); + var promise = new Promise(); promise.WithName(Name); - this.Then(() => { promise.Resolve(); }); - this.Catch((e) => { promise.Resolve(); }); + this.Then(() => promise.Resolve()); + this.Catch(e => promise.Resolve()); - return promise.Then(() => { return onComplete(); }); + return promise.Then(onComplete); } public IPromise Progress(Action onProgress) diff --git a/Tests/PromiseProgressTests.cs b/Tests/PromiseProgressTests.cs index edd545e..b2e7e74 100644 --- a/Tests/PromiseProgressTests.cs +++ b/Tests/PromiseProgressTests.cs @@ -8,7 +8,7 @@ public class PromiseProgressTests [Fact] public void can_report_simple_progress() { - var expectedStep = 0.25f; + const float expectedStep = 0.25f; var currentProgress = 0f; var promise = new Promise(); @@ -18,7 +18,7 @@ public void can_report_simple_progress() currentProgress = v; }); - for (float progress = 0.25f; progress < 1f; progress += 0.25f) + for (var progress = 0.25f; progress < 1f; progress += 0.25f) promise.ReportProgress(progress); promise.ReportProgress(1f); @@ -70,7 +70,7 @@ public void can_do_progress_weighted_average() var promiseB = new Promise(); var promiseC = new Promise(); - var expectedProgress = new float[] { 0.1f, 0.2f, 0.6f, 1f }; + var expectedProgress = new[] { 0.1f, 0.2f, 0.6f, 1f }; var currentProgress = 0f; int currentStep = 0; int result = 0; @@ -162,9 +162,9 @@ public void all_progress_is_averaged() var promiseD = new Promise(); int currentStep = 0; - var expectedProgress = new float[] { 0.25f, 0.50f, 0.75f, 1f }; + var expectedProgress = new[] { 0.25f, 0.50f, 0.75f, 1f }; - Promise.All(new IPromise[] { promiseA, promiseB, promiseC, promiseD }) + Promise.All(promiseA, promiseB, promiseC, promiseD) .Progress(progress => { Assert.InRange(currentStep, 0, expectedProgress.Length - 1); @@ -188,7 +188,7 @@ public void race_progress_is_maxed() var promiseB = new Promise(); int reportCount = 0; - Promise.Race(new IPromise[] { promiseA, promiseB }) + Promise.Race(promiseA, promiseB) .Progress(progress => { Assert.Equal(progress, 0.5f); @@ -212,7 +212,7 @@ public void all_progress_with_resolved() var promiseB = Promise.Resolved(17); int reportedCount = 0; - Promise.All(new IPromise[] { promiseA, promiseB }) + Promise.All(promiseA, promiseB) .Progress(progress => { ++reportedCount; diff --git a/Tests/PromiseTests.cs b/Tests/PromiseTests.cs index 4de56bf..17ce9d7 100644 --- a/Tests/PromiseTests.cs +++ b/Tests/PromiseTests.cs @@ -1,10 +1,6 @@ -using Moq; -using RSG; using RSG.Promises; using System; -using System.Collections.Generic; using System.Linq; -using System.Text; using Xunit; namespace RSG.Tests @@ -14,7 +10,7 @@ public class PromiseTests [Fact] public void can_resolve_simple_promise() { - var promisedValue = 5; + const int promisedValue = 5; var promise = Promise.Resolved(promisedValue); var completed = 0; @@ -85,7 +81,7 @@ public void can_resolve_promise_and_trigger_then_handler() var promise = new Promise(); var completed = 0; - var promisedValue = 15; + const int promisedValue = 15; promise.Then(v => { @@ -209,10 +205,7 @@ public void error_handler_is_not_invoked_for_resolved_promised() { var promise = new Promise(); - promise.Catch(e => - { - throw new ApplicationException("This shouldn't happen"); - }); + promise.Catch(e => throw new ApplicationException("This shouldn't happen")); promise.Resolve(5); } @@ -222,10 +215,7 @@ public void then_handler_is_not_invoked_for_rejected_promise() { var promise = new Promise(); - promise.Then(v => - { - throw new ApplicationException("This shouldn't happen"); - }); + promise.Then(v => throw new ApplicationException("This shouldn't happen")); promise.Reject(new ApplicationException("Rejection!")); } @@ -236,8 +226,8 @@ public void chain_multiple_promises_using_all() var promise = new Promise(); var chainedPromise1 = new Promise(); var chainedPromise2 = new Promise(); - var chainedResult1 = 10; - var chainedResult2 = 15; + const int chainedResult1 = 10; + const int chainedResult2 = 15; var completed = 0; @@ -275,8 +265,8 @@ public void chain_multiple_promises_using_all_that_are_resolved_out_of_order() var promise = new Promise(); var chainedPromise1 = new Promise(); var chainedPromise2 = new Promise(); - var chainedResult1 = 10; - var chainedResult2 = 15; + const int chainedResult1 = 10; + const int chainedResult2 = 15; var completed = 0; @@ -453,10 +443,7 @@ public void combined_promise_is_rejected_when_first_promise_is_rejected() var all = Promise.All(EnumerableExt.FromItems>(promise1, promise2)); - all.Then(v => - { - throw new ApplicationException("Shouldn't happen"); - }); + all.Then(v => throw new ApplicationException("Shouldn't happen")); var errors = 0; all.Catch(e => @@ -478,10 +465,7 @@ public void combined_promise_of_multiple_types_is_rejected_when_first_promise_is var all = PromiseHelpers.All(promise1, promise2); - all.Then(v => - { - throw new ApplicationException("Shouldn't happen"); - }); + all.Then(v => throw new ApplicationException("Shouldn't happen")); var errors = 0; all.Catch(e => @@ -503,10 +487,7 @@ public void combined_promise_is_rejected_when_second_promise_is_rejected() var all = Promise.All(EnumerableExt.FromItems>(promise1, promise2)); - all.Then(v => - { - throw new ApplicationException("Shouldn't happen"); - }); + all.Then(v => throw new ApplicationException("Shouldn't happen")); var errors = 0; all.Catch(e => @@ -528,10 +509,7 @@ public void combined_promise_of_multiple_types_is_rejected_when_second_promise_i var all = PromiseHelpers.All(promise1, promise2); - all.Then(v => - { - throw new ApplicationException("Shouldn't happen"); - }); + all.Then(v => throw new ApplicationException("Shouldn't happen")); var errors = 0; all.Catch(e => @@ -553,10 +531,7 @@ public void combined_promise_is_rejected_when_both_promises_are_rejected() var all = Promise.All(EnumerableExt.FromItems>(promise1, promise2)); - all.Then(v => - { - throw new ApplicationException("Shouldn't happen"); - }); + all.Then(v => throw new ApplicationException("Shouldn't happen")); var errors = 0; all.Catch(e => @@ -578,10 +553,7 @@ public void combined_promise_of_multiple_types_is_rejected_when_both_promises_ar var all = PromiseHelpers.All(promise1, promise2); - all.Then(v => - { - throw new ApplicationException("Shouldn't happen"); - }); + all.Then(v => throw new ApplicationException("Shouldn't happen")); var errors = 0; all.Catch(e => @@ -702,15 +674,12 @@ public void exception_thrown_during_transform_rejects_transformed_promise() { var promise = new Promise(); - var promisedValue = 15; + const int promisedValue = 15; var errors = 0; var ex = new Exception(); promise - .Then(v => - { - throw ex; - }) + .Then(v => throw ex) .Catch(e => { Assert.Equal(ex, e); @@ -729,8 +698,8 @@ public void can_chain_promise_and_convert_type_of_value() var promise = new Promise(); var chainedPromise = new Promise(); - var promisedValue = 15; - var chainedPromiseValue = "blah"; + const int promisedValue = 15; + const string chainedPromiseValue = "blah"; var completed = 0; promise @@ -754,7 +723,7 @@ public void can_chain_promise_and_convert_to_non_value_promise() var promise = new Promise(); var chainedPromise = new Promise(); - var promisedValue = 15; + const int promisedValue = 15; var completed = 0; promise @@ -774,16 +743,12 @@ public void can_chain_promise_and_convert_to_non_value_promise() public void exception_thrown_in_chain_rejects_resulting_promise() { var promise = new Promise(); - var chainedPromise = new Promise(); var ex = new Exception(); var errors = 0; promise - .Then(v => - { - throw ex; - }) + .Then(v => throw ex) .Catch(e => { Assert.Equal(ex, e); @@ -930,10 +895,7 @@ public void can_reject_promise_via_reject_function() public void exception_thrown_during_resolver_rejects_promise() { var ex = new Exception(); - var promise = new Promise((resolve, reject) => - { - throw ex; - }); + var promise = new Promise((resolve, reject) => throw ex); var completed = 0; promise.Catch(e => @@ -964,10 +926,7 @@ public void unhandled_exception_is_propagated_via_event() try { promise - .Then(a => - { - throw ex; - }) + .Then(a => throw ex) .Done(); promise.Resolve(5); @@ -999,10 +958,7 @@ public void exception_in_done_callback_is_propagated_via_event() try { promise - .Done(x => - { - throw ex; - }); + .Done(x => throw ex); promise.Resolve(5); @@ -1028,10 +984,7 @@ public void handled_exception_is_not_propagated_via_event() try { promise - .Then(a => - { - throw ex; - }) + .Then(a => throw ex) .Catch(_ => { // Catch the error. @@ -1054,7 +1007,7 @@ public void can_handle_Done_onResolved() { var promise = new Promise(); var callback = 0; - var expectedValue = 5; + const int expectedValue = 5; promise.Done(value => { @@ -1074,7 +1027,7 @@ public void can_handle_Done_onResolved_with_onReject() var promise = new Promise(); var callback = 0; var errorCallback = 0; - var expectedValue = 5; + const int expectedValue = 5; promise.Done( value => @@ -1140,10 +1093,7 @@ public void exception_during_Then_onResolved_triggers_error_hander() var expectedException = new Exception(); promise - .Then(value => - { - throw expectedException; - }) + .Then(value => throw expectedException) .Done( () => { @@ -1273,12 +1223,12 @@ public void rejected_chain_continues_after_finally_returning_value_promise() { var promise = new Promise(); var callback = 0; - var expectedValue = 42; + const int expectedValue = 42; promise.Finally(() => { ++callback; return Promise.Resolved(expectedValue); }) - .Then((x) => { + .Then(x => { Assert.Equal(expectedValue, (x)); ++callback; }); @@ -1292,7 +1242,7 @@ public void rejected_chain_continues_after_finally_returning_value_promise() public void can_chain_promise_generic_after_finally() { var promise = new Promise(); - var expectedValue = 5; + const int expectedValue = 5; var callback = 0; promise.Finally(() => @@ -1300,7 +1250,7 @@ public void can_chain_promise_generic_after_finally() ++callback; return Promise.Resolved(expectedValue); }) - .Then((x) => + .Then(x => { Assert.Equal(expectedValue, x); ++callback; @@ -1369,11 +1319,11 @@ public void exception_in_finally_callback_returning_non_value_promise_is_caught_ var callback = 0; var expectedException = new Exception("Expected"); - promise.Finally(new Func(() => + promise.Finally(() => { ++callback; throw expectedException; - })) + }) .Catch(ex => { Assert.Equal(expectedException, ex); @@ -1419,7 +1369,7 @@ public void exception_in_reject_callback_is_caught_by_chained_catch() new Promise((res, rej) => rej(new Exception())) .Then( _ => Promise.Resolved(null), - _ => { throw expectedException; } + _ => throw expectedException ) .Catch(ex => actualException = ex); diff --git a/Tests/PromiseTimerTests.cs b/Tests/PromiseTimerTests.cs index 06e59f6..b91edd6 100644 --- a/Tests/PromiseTimerTests.cs +++ b/Tests/PromiseTimerTests.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using Xunit; namespace RSG.Tests @@ -13,10 +10,10 @@ public void wait_until_elapsedUpdates_resolves_when_predicate_is_true() { var testObject = new PromiseTimer(); - var testFrame = 3; + const int testFrame = 3; var hasResolved = false; - testObject.WaitUntil((timeData) => timeData.elapsedUpdates == testFrame) + testObject.WaitUntil(timeData => timeData.elapsedUpdates == testFrame) .Then(() => hasResolved = true) .Done(); @@ -34,7 +31,7 @@ public void wait_for_doesnt_resolve_before_specified_time() { var testObject = new PromiseTimer(); - var testTime = 2f; + const float testTime = 2f; var hasResolved = false; testObject.WaitFor(testTime) @@ -51,7 +48,7 @@ public void wait_for_resolves_after_specified_time() { var testObject = new PromiseTimer(); - var testTime = 1f; + const float testTime = 1f; var hasResolved = false; testObject.WaitFor(testTime) @@ -183,10 +180,7 @@ public void when_predicate_throws_exception_reject_promise() testObject - .WaitUntil(timeData => - { - throw expectedException; - }) + .WaitUntil(timeData => throw expectedException) .Catch(ex => caughtException = ex) .Done(); diff --git a/Tests/Promise_NonGeneric_ProgressTests.cs b/Tests/Promise_NonGeneric_ProgressTests.cs index 3b3962b..51ff829 100644 --- a/Tests/Promise_NonGeneric_ProgressTests.cs +++ b/Tests/Promise_NonGeneric_ProgressTests.cs @@ -8,7 +8,7 @@ public class Promise_NonGeneric_ProgressTests [Fact] public void can_report_simple_progress() { - var expectedStep = 0.25f; + const float expectedStep = 0.25f; var currentProgress = 0f; var promise = new Promise(); @@ -18,7 +18,7 @@ public void can_report_simple_progress() currentProgress = v; }); - for (float progress = 0.25f; progress < 1f; progress += 0.25f) + for (var progress = 0.25f; progress < 1f; progress += 0.25f) promise.ReportProgress(progress); promise.ReportProgress(1f); @@ -67,7 +67,7 @@ public void can_do_progress_weighted_average() var promiseB = new Promise(); var promiseC = new Promise(); - var expectedProgress = new float[] { 0.1f, 0.2f, 0.6f, 1f }; + var expectedProgress = new[] { 0.1f, 0.2f, 0.6f, 1f }; var currentProgress = 0f; int currentStep = 0; @@ -151,9 +151,9 @@ public void all_progress_is_averaged() var promiseD = new Promise(); int currentStep = 0; - var expectedProgress = new float[] { 0.25f, 0.50f, 0.75f, 1f }; + var expectedProgress = new[] { 0.25f, 0.50f, 0.75f, 1f }; - Promise.All(new IPromise[] { promiseA, promiseB, promiseC, promiseD }) + Promise.All(promiseA, promiseB, promiseC, promiseD) .Progress(progress => { Assert.InRange(currentStep, 0, expectedProgress.Length - 1); @@ -177,7 +177,7 @@ public void race_progress_is_maxed() var promiseB = new Promise(); int reportCount = 0; - Promise.Race(new IPromise[] { promiseA, promiseB }) + Promise.Race(promiseA, promiseB) .Progress(progress => { Assert.Equal(progress, 0.5f); @@ -201,7 +201,7 @@ public void all_progress_with_resolved() var promiseB = Promise.Resolved(); int reportedCount = 0; - Promise.All(new IPromise[] { promiseA, promiseB }) + Promise.All(promiseA, promiseB) .Progress(progress => { ++reportedCount; diff --git a/Tests/Promise_NonGeneric_Tests.cs b/Tests/Promise_NonGeneric_Tests.cs index 065292a..deb38b9 100644 --- a/Tests/Promise_NonGeneric_Tests.cs +++ b/Tests/Promise_NonGeneric_Tests.cs @@ -1,10 +1,6 @@ -using Moq; -using RSG; using RSG.Promises; using System; -using System.Collections.Generic; using System.Linq; -using System.Text; using Xunit; namespace RSG.Tests @@ -194,10 +190,7 @@ public void error_handler_is_not_invoked_for_resolved_promised() { var promise = new Promise(); - promise.Catch(e => - { - throw new ApplicationException("This shouldn't happen"); - }); + promise.Catch(e => throw new ApplicationException("This shouldn't happen")); promise.Resolve(); } @@ -207,10 +200,7 @@ public void then_handler_is_not_invoked_for_rejected_promise() { var promise = new Promise(); - promise.Then(() => - { - throw new ApplicationException("This shouldn't happen"); - }); + promise.Then(() => throw new ApplicationException("This shouldn't happen")); promise.Reject(new ApplicationException("Rejection!")); } @@ -249,8 +239,8 @@ public void chain_multiple_promises_using_all_that_are_resolved_out_of_order() var promise = new Promise(); var chainedPromise1 = new Promise(); var chainedPromise2 = new Promise(); - var chainedResult1 = 10; - var chainedResult2 = 15; + const int chainedResult1 = 10; + const int chainedResult2 = 15; var completed = 0; @@ -287,8 +277,8 @@ public void chain_multiple_value_promises_using_all_resolved_out_of_order() var promise = new Promise(); var chainedPromise1 = new Promise(); var chainedPromise2 = new Promise(); - var chainedResult1 = 10; - var chainedResult2 = 15; + const int chainedResult1 = 10; + const int chainedResult2 = 15; var completed = 0; @@ -345,10 +335,7 @@ public void combined_promise_is_rejected_when_first_promise_is_rejected() var all = Promise.All(EnumerableExt.FromItems(promise1, promise2)); - all.Then(() => - { - throw new ApplicationException("Shouldn't happen"); - }); + all.Then(() => throw new ApplicationException("Shouldn't happen")); var errors = 0; all.Catch(e => @@ -370,10 +357,7 @@ public void combined_promise_is_rejected_when_second_promise_is_rejected() var all = Promise.All(EnumerableExt.FromItems(promise1, promise2)); - all.Then(() => - { - throw new ApplicationException("Shouldn't happen"); - }); + all.Then(() => throw new ApplicationException("Shouldn't happen")); var errors = 0; all.Catch(e => @@ -395,16 +379,10 @@ public void combined_promise_is_rejected_when_both_promises_are_rejected() var all = Promise.All(EnumerableExt.FromItems(promise1, promise2)); - all.Then(() => - { - throw new ApplicationException("Shouldn't happen"); - }); + all.Then(() => throw new ApplicationException("Shouldn't happen")); var errors = 0; - all.Catch(e => - { - ++errors; - }); + all.Catch(e => ++errors); promise1.Reject(new ApplicationException("Error!")); promise2.Reject(new ApplicationException("Error!")); @@ -420,6 +398,8 @@ public void combined_promise_is_resolved_if_there_are_no_promises() var completed = 0; all.Then(() => ++completed); + + Assert.Equal(1, completed); } [Fact] @@ -448,11 +428,8 @@ public void exception_thrown_during_transform_rejects_promise() var errors = 0; var ex = new Exception(); - var transformedPromise = promise - .Then(() => - { - throw ex; - }) + promise + .Then(() => throw ex) .Catch(e => { Assert.Equal(ex, e); @@ -511,16 +488,12 @@ public void can_chain_promise_and_convert_to_promise_that_yields_a_value() public void exception_thrown_in_chain_rejects_resulting_promise() { var promise = new Promise(); - var chainedPromise = new Promise(); var ex = new Exception(); var errors = 0; promise - .Then(() => - { - throw ex; - }) + .Then(() => throw ex) .Catch(e => { Assert.Equal(ex, e); @@ -632,7 +605,7 @@ public void sequence_with_no_operations_is_directly_resolved() var completed = 0; Promise - .Sequence(new Func[0]) + .Sequence() .Then(() => ++completed); Assert.Equal(1, completed); @@ -656,7 +629,7 @@ public void sequence_is_resolved_when_operation_is_resolved() var completed = 0; Promise - .Sequence(() => Promise.Resolved()) + .Sequence(Promise.Resolved) .Then(() => ++completed); Assert.Equal(1, completed); @@ -669,7 +642,7 @@ public void sequence_is_unresolved_when_some_operations_are_unresolved() Promise .Sequence( - () => Promise.Resolved(), + Promise.Resolved, () => new Promise() ) .Then(() => ++completed); @@ -683,10 +656,7 @@ public void sequence_is_resolved_when_all_operations_are_resolved() var completed = 0; Promise - .Sequence( - () => Promise.Resolved(), - () => Promise.Resolved() - ) + .Sequence(Promise.Resolved, Promise.Resolved) .Then(() => ++completed); Assert.Equal(1, completed); @@ -727,10 +697,7 @@ public void exception_thrown_in_sequence_rejects_the_promise() var ex = new Exception(); Promise - .Sequence(() => - { - throw ex; - }) + .Sequence(() => throw ex) .Then(() => ++completed) .Catch(e => { @@ -754,10 +721,7 @@ public void exception_thrown_in_sequence_stops_following_operations_from_being_i ++completed; return Promise.Resolved(); }, - () => - { - throw new Exception(); - }, + () => throw new Exception(), () => { ++completed; @@ -808,10 +772,7 @@ public void can_reject_promise_via_reject_function() public void exception_thrown_during_resolver_rejects_proimse() { var ex = new Exception(); - var promise = new Promise((resolve, reject) => - { - throw ex; - }); + var promise = new Promise((resolve, reject) => throw ex); var completed = 0; promise.Catch(e => @@ -842,10 +803,7 @@ public void unhandled_exception_is_propagated_via_event() try { promise - .Then(() => - { - throw ex; - }) + .Then(() => throw ex) .Done(); promise.Resolve(); @@ -877,10 +835,7 @@ public void exception_in_done_callback_is_propagated_via_event() try { promise - .Done(() => - { - throw ex; - }); + .Done(() => throw ex); promise.Resolve(); @@ -906,10 +861,7 @@ public void handled_exception_is_not_propagated_via_event() try { promise - .Then(() => - { - throw ex; - }) + .Then(() => throw ex) .Catch(_ => { // Catch the error. @@ -1003,10 +955,7 @@ public void exception_during_Then_onResolved_triggers_error_hander() var expectedException = new Exception(); promise - .Then(() => - { - throw expectedException; - }) + .Then(() => throw expectedException) .Done( () => ++callback, ex => @@ -1041,10 +990,7 @@ public void inner_exception_handled_by_outer_promise() promise .Then(() => { - return Promise.Resolved().Then(() => - { - throw expectedException; - }); + return Promise.Resolved().Then(() => throw expectedException); }) .Catch(ex => { @@ -1083,13 +1029,7 @@ public void inner_exception_handled_by_outer_promise_with_results() try { promise - .Then((_) => - { - return Promise.Resolved(5).Then((__) => - { - throw expectedException; - }); - }) + .Then(_ => Promise.Resolved(5).Then(__ => throw expectedException)) .Catch(ex => { Assert.Equal(expectedException, ex); @@ -1221,7 +1161,7 @@ public void rejected_chain_continues_after_finally_returning_value_promise() { ++callback; return Promise.Resolved("foo"); }) - .Then((x) => { + .Then(x => { Assert.Equal(expectedValue, x); ++callback; }); @@ -1267,11 +1207,11 @@ public void exception_in_finally_callback_returning_non_value_promise_is_caught_ var callback = 0; var expectedException = new Exception("Expected"); - promise.Finally(new Func(() => + promise.Finally(() => { ++callback; throw expectedException; - })) + }) .Catch(ex => { Assert.Equal(expectedException, ex); @@ -1286,7 +1226,7 @@ public void exception_in_finally_callback_returning_non_value_promise_is_caught_ [Fact] public void exception_in_finally_callback_returning_value_promise_is_caught_by_chained_catch() { - //NOTE: Also tests that the new exception is passed thru promise chain + //NOTE: Also tests that the new exception is passed through promise chain var promise = new Promise(); var callback = 0; @@ -1312,7 +1252,7 @@ public void exception_in_finally_callback_returning_value_promise_is_caught_by_c public void can_chain_promise_after_finally() { var promise = new Promise(); - var expectedValue = 5; + const int expectedValue = 5; var callback = 0; promise.Finally(() => @@ -1320,7 +1260,7 @@ public void can_chain_promise_after_finally() ++callback; return Promise.Resolved(expectedValue); }) - .Then((x) => + .Then(x => { Assert.Equal(expectedValue, x); ++callback; From 84d917f717af1eb746db16bcd7435d01cc4b7fa0 Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Mon, 12 Feb 2018 11:07:19 +1000 Subject: [PATCH 54/80] Renamed old version of Finally ContinueWith --- Promise.cs | 20 ++++++++----------- Promise_NonGeneric.cs | 20 ++++++++----------- Tests/PromiseTests.cs | 32 +++++++++++++++---------------- Tests/Promise_NonGeneric_Tests.cs | 32 +++++++++++++++---------------- 4 files changed, 46 insertions(+), 58 deletions(-) diff --git a/Promise.cs b/Promise.cs index 403bcaa..f327c1c 100644 --- a/Promise.cs +++ b/Promise.cs @@ -151,20 +151,18 @@ Action onProgress IPromise Finally(Action onComplete); /// - /// Add a finally callback that chains a non-value promise. - /// Finally callbacks will always be called, even if any preceding promise is rejected, or encounters an error. + /// Add a callback that chains a non-value promise. + /// ContinueWith callbacks will always be called, even if any preceding promise is rejected, or encounters an error. /// The state of the returning promise will be based on the new non-value promise, not the preceding (rejected or resolved) promise. /// - [Obsolete("Use new version of Finally that is TC39 compliant, or Catch and Then.")] - IPromise Finally(Func onResolved); + IPromise ContinueWith(Func onResolved); /// - /// Add a finally callback that chains a value promise (optionally converting to a different value type). - /// Finally callbacks will always be called, even if any preceding promise is rejected, or encounters an error. + /// Add a callback that chains a value promise (optionally converting to a different value type). + /// ContinueWith callbacks will always be called, even if any preceding promise is rejected, or encounters an error. /// The state of the returning promise will be based on the new value promise, not the preceding (rejected or resolved) promise. /// - [Obsolete("Use new version of Finally that is TC39 compliant, or Catch and Then.")] - IPromise Finally(Func> onComplete); + IPromise ContinueWith(Func> onComplete); /// /// Add a progress callback. @@ -1005,8 +1003,7 @@ public IPromise Finally(Action onComplete) return promise.Then(_ => onComplete()); } - [Obsolete("Use new version of Finally that is TC39 compliant, or Catch and Then.")] - public IPromise Finally(Func onComplete) + public IPromise ContinueWith(Func onComplete) { var promise = new Promise(); promise.WithName(Name); @@ -1017,8 +1014,7 @@ public IPromise Finally(Func onComplete) return promise.Then(onComplete); } - [Obsolete("Use new version of Finally that is TC39 compliant, or Catch and Then.")] - public IPromise Finally(Func> onComplete) + public IPromise ContinueWith(Func> onComplete) { var promise = new Promise(); promise.WithName(Name); diff --git a/Promise_NonGeneric.cs b/Promise_NonGeneric.cs index ca35ab1..e51011e 100644 --- a/Promise_NonGeneric.cs +++ b/Promise_NonGeneric.cs @@ -138,20 +138,18 @@ public interface IPromise IPromise Finally(Action onComplete); /// - /// Add a finally callback that chains a non-value promise. - /// Finally callbacks will always be called, even if any preceding promise is rejected, or encounters an error. + /// Add a callback that chains a non-value promise. + /// ContinueWith callbacks will always be called, even if any preceding promise is rejected, or encounters an error. /// The state of the returning promise will be based on the new non-value promise, not the preceding (rejected or resolved) promise. /// - [Obsolete("Use new version of Finally that is TC39 compliant, or Catch and Then.")] - IPromise Finally(Func onResolved); + IPromise ContinueWith(Func onResolved); /// - /// Add a finally callback that chains a value promise (optionally converting to a different value type). - /// Finally callbacks will always be called, even if any preceding promise is rejected, or encounters an error. + /// Add a callback that chains a value promise (optionally converting to a different value type). + /// ContinueWith callbacks will always be called, even if any preceding promise is rejected, or encounters an error. /// The state of the returning promise will be based on the new value promise, not the preceding (rejected or resolved) promise. /// - [Obsolete("Use new version of Finally that is TC39 compliant, or Catch and Then.")] - IPromise Finally(Func> onComplete); + IPromise ContinueWith(Func> onComplete); /// /// Add a progress callback. @@ -1096,8 +1094,7 @@ public IPromise Finally(Action onComplete) return promise.Then(onComplete); } - [Obsolete("Use new version of Finally that is TC39 compliant, or Catch and Then.")] - public IPromise Finally(Func onComplete) + public IPromise ContinueWith(Func onComplete) { var promise = new Promise(); promise.WithName(Name); @@ -1108,8 +1105,7 @@ public IPromise Finally(Func onComplete) return promise.Then(onComplete); } - [Obsolete("Use new version of Finally that is TC39 compliant, or Catch and Then.")] - public IPromise Finally(Func> onComplete) + public IPromise ContinueWith(Func> onComplete) { var promise = new Promise(); promise.WithName(Name); diff --git a/Tests/PromiseTests.cs b/Tests/PromiseTests.cs index 17ce9d7..04d0812 100644 --- a/Tests/PromiseTests.cs +++ b/Tests/PromiseTests.cs @@ -1161,7 +1161,7 @@ public void resolved_chain_continues_after_finally() { var promise = new Promise(); var callback = 0; - var expectedValue = 42; + const int expectedValue = 42; promise.Finally(() => { @@ -1200,12 +1200,12 @@ public void rejected_chain_rejects_after_finally() } [Fact] - public void rejected_chain_continues_after_finally_returning_non_value_promise() + public void rejected_chain_continues_after_ContinueWith_returning_non_value_promise() { var promise = new Promise(); var callback = 0; - promise.Finally(() => { + promise.ContinueWith(() => { ++callback; return Promise.Resolved(); }) @@ -1219,17 +1219,17 @@ public void rejected_chain_continues_after_finally_returning_non_value_promise() } [Fact] - public void rejected_chain_continues_after_finally_returning_value_promise() + public void rejected_chain_continues_after_ContinueWith_returning_value_promise() { var promise = new Promise(); var callback = 0; const int expectedValue = 42; - promise.Finally(() => { + promise.ContinueWith(() => { ++callback; return Promise.Resolved(expectedValue); }) .Then(x => { - Assert.Equal(expectedValue, (x)); + Assert.Equal(expectedValue, x); ++callback; }); @@ -1245,7 +1245,7 @@ public void can_chain_promise_generic_after_finally() const int expectedValue = 5; var callback = 0; - promise.Finally(() => + promise.ContinueWith(() => { ++callback; return Promise.Resolved(expectedValue); @@ -1271,9 +1271,8 @@ public void can_chain_promise_after_finally() promise.Finally(() => { ++callback; - return Promise.Resolved(); }) - .Then(() => + .Then(_ => { ++callback; }); @@ -1293,12 +1292,11 @@ public void exception_in_finally_callback_is_caught_by_chained_catch() var callback = 0; var expectedException = new Exception("Expected"); - // NOTE: typecast needed to call Finally(Action), and not Finally(Func) - promise.Finally((Action)(() => + promise.Finally(() => { ++callback; throw expectedException; - })) + }) .Catch(ex => { Assert.Equal(expectedException, ex); @@ -1311,7 +1309,7 @@ public void exception_in_finally_callback_is_caught_by_chained_catch() } [Fact] - public void exception_in_finally_callback_returning_non_value_promise_is_caught_by_chained_catch() + public void exception_in_ContinueWith_callback_returning_non_value_promise_is_caught_by_chained_catch() { //NOTE: Also tests that the new exception is passed thru promise chain @@ -1319,7 +1317,7 @@ public void exception_in_finally_callback_returning_non_value_promise_is_caught_ var callback = 0; var expectedException = new Exception("Expected"); - promise.Finally(() => + promise.ContinueWith(() => { ++callback; throw expectedException; @@ -1336,15 +1334,15 @@ public void exception_in_finally_callback_returning_non_value_promise_is_caught_ } [Fact] - public void exception_in_finally_callback_returning_value_promise_is_caught_by_chained_catch() + public void exception_in_ContinueWith_callback_returning_value_promise_is_caught_by_chained_catch() { - //NOTE: Also tests that the new exception is passed thru promise chain + // NOTE: Also tests that the new exception is passed through promise chain var promise = new Promise(); var callback = 0; var expectedException = new Exception("Expected"); - promise.Finally(new Func>(() => + promise.ContinueWith(new Func>(() => { ++callback; throw expectedException; diff --git a/Tests/Promise_NonGeneric_Tests.cs b/Tests/Promise_NonGeneric_Tests.cs index deb38b9..512c482 100644 --- a/Tests/Promise_NonGeneric_Tests.cs +++ b/Tests/Promise_NonGeneric_Tests.cs @@ -1134,17 +1134,16 @@ public void rejected_chain_rejects_after_finally() } [Fact] - public void rejected_chain_continues_after_finally_returning_non_value_promise() { + public void rejected_chain_continues_after_ContinueWith_returning_non_value_promise() { var promise = new Promise(); var callback = 0; - promise.Finally(() => { + promise.ContinueWith(() => + { ++callback; return Promise.Resolved(); }) - .Then(() => { - ++callback; - }); + .Then(() => ++callback); promise.Reject(new Exception()); @@ -1152,12 +1151,12 @@ public void rejected_chain_continues_after_finally_returning_non_value_promise() } [Fact] - public void rejected_chain_continues_after_finally_returning_value_promise() { + public void rejected_chain_continues_after_ContinueWith_returning_value_promise() { var promise = new Promise(); var callback = 0; - var expectedValue = "foo"; + const string expectedValue = "foo"; - promise.Finally(() => { + promise.ContinueWith(() => { ++callback; return Promise.Resolved("foo"); }) @@ -1181,12 +1180,11 @@ public void exception_in_finally_callback_is_caught_by_chained_catch() var callback = 0; var expectedException = new Exception("Expected"); - // NOTE: typecast needed to call Finally(Action), and not Finally(Func) - promise.Finally((Action)(() => + promise.Finally(() => { ++callback; throw expectedException; - })) + }) .Catch(ex => { Assert.Equal(expectedException, ex); @@ -1199,7 +1197,7 @@ public void exception_in_finally_callback_is_caught_by_chained_catch() } [Fact] - public void exception_in_finally_callback_returning_non_value_promise_is_caught_by_chained_catch() + public void exception_in_ContinueWith_callback_returning_non_value_promise_is_caught_by_chained_catch() { //NOTE: Also tests that the new exception is passed thru promise chain @@ -1207,7 +1205,7 @@ public void exception_in_finally_callback_returning_non_value_promise_is_caught_ var callback = 0; var expectedException = new Exception("Expected"); - promise.Finally(() => + promise.ContinueWith(() => { ++callback; throw expectedException; @@ -1224,7 +1222,7 @@ public void exception_in_finally_callback_returning_non_value_promise_is_caught_ } [Fact] - public void exception_in_finally_callback_returning_value_promise_is_caught_by_chained_catch() + public void exception_in_ContinueWith_callback_returning_value_promise_is_caught_by_chained_catch() { //NOTE: Also tests that the new exception is passed through promise chain @@ -1232,7 +1230,7 @@ public void exception_in_finally_callback_returning_value_promise_is_caught_by_c var callback = 0; var expectedException = new Exception("Expected"); - promise.Finally(new Func>(() => + promise.ContinueWith(new Func>(() => { ++callback; throw expectedException; @@ -1249,13 +1247,13 @@ public void exception_in_finally_callback_returning_value_promise_is_caught_by_c } [Fact] - public void can_chain_promise_after_finally() + public void can_chain_promise_after_ContinueWith() { var promise = new Promise(); const int expectedValue = 5; var callback = 0; - promise.Finally(() => + promise.ContinueWith(() => { ++callback; return Promise.Resolved(expectedValue); From 7983de31d15275e922523699b728e1c79b3fdbf8 Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Mon, 12 Feb 2018 11:17:07 +1000 Subject: [PATCH 55/80] Simplified some more code in tests --- Tests/PromiseTests.cs | 121 +++++++++--------------------- Tests/Promise_NonGeneric_Tests.cs | 51 +++---------- 2 files changed, 46 insertions(+), 126 deletions(-) diff --git a/Tests/PromiseTests.cs b/Tests/PromiseTests.cs index 04d0812..81b6196 100644 --- a/Tests/PromiseTests.cs +++ b/Tests/PromiseTests.cs @@ -70,9 +70,7 @@ public void exception_is_thrown_for_resolve_after_reject() promise.Reject(new ApplicationException()); - Assert.Throws(() => - promise.Resolve(5) - ); + Assert.Throws(() => promise.Resolve(5)); } [Fact] @@ -101,9 +99,7 @@ public void exception_is_thrown_for_resolve_after_resolve() promise.Resolve(5); - Assert.Throws(() => - promise.Resolve(5) - ); + Assert.Throws(() => promise.Resolve(5)); } [Fact] @@ -127,7 +123,7 @@ public void can_resolve_promise_and_trigger_then_handler_with_callback_registrat var promise = new Promise(); var completed = 0; - var promisedValue = -10; + const int promisedValue = -10; promise.Resolve(promisedValue); @@ -308,10 +304,7 @@ public void chain_multiple_promises_using_all_and_convert_to_non_value_promise() promise .ThenAll(i => EnumerableExt.FromItems(chainedPromise1, chainedPromise2).Cast()) - .Then(() => - { - ++completed; - }); + .Then(() => ++completed); Assert.Equal(0, completed); @@ -446,10 +439,7 @@ public void combined_promise_is_rejected_when_first_promise_is_rejected() all.Then(v => throw new ApplicationException("Shouldn't happen")); var errors = 0; - all.Catch(e => - { - ++errors; - }); + all.Catch(e => ++errors); promise1.Reject(new ApplicationException("Error!")); promise2.Resolve(2); @@ -468,10 +458,7 @@ public void combined_promise_of_multiple_types_is_rejected_when_first_promise_is all.Then(v => throw new ApplicationException("Shouldn't happen")); var errors = 0; - all.Catch(e => - { - ++errors; - }); + all.Catch(e => ++errors); promise1.Reject(new ApplicationException("Error!")); promise2.Resolve(true); @@ -490,10 +477,7 @@ public void combined_promise_is_rejected_when_second_promise_is_rejected() all.Then(v => throw new ApplicationException("Shouldn't happen")); var errors = 0; - all.Catch(e => - { - ++errors; - }); + all.Catch(e => ++errors); promise1.Resolve(2); promise2.Reject(new ApplicationException("Error!")); @@ -512,10 +496,7 @@ public void combined_promise_of_multiple_types_is_rejected_when_second_promise_i all.Then(v => throw new ApplicationException("Shouldn't happen")); var errors = 0; - all.Catch(e => - { - ++errors; - }); + all.Catch(e => ++errors); promise1.Resolve(2); promise2.Reject(new ApplicationException("Error!")); @@ -556,10 +537,7 @@ public void combined_promise_of_multiple_types_is_rejected_when_both_promises_ar all.Then(v => throw new ApplicationException("Shouldn't happen")); var errors = 0; - all.Catch(e => - { - ++errors; - }); + all.Catch(e => ++errors); promise1.Reject(new ApplicationException("Error!")); promise2.Reject(new ApplicationException("Error!")); @@ -728,10 +706,7 @@ public void can_chain_promise_and_convert_to_non_value_promise() promise .Then(v => (IPromise)chainedPromise) - .Then(() => - { - ++completed; - }); + .Then(() => ++completed); promise.Resolve(promisedValue); chainedPromise.Resolve(); @@ -857,10 +832,7 @@ public void race_is_rejected_when_second_promise_is_rejected_first() [Fact] public void can_resolve_promise_via_resolver_function() { - var promise = new Promise((resolve, reject) => - { - resolve(5); - }); + var promise = new Promise((resolve, reject) => resolve(5)); var completed = 0; promise.Then(v => @@ -1036,10 +1008,7 @@ public void can_handle_Done_onResolved_with_onReject() ++callback; }, - ex => - { - ++errorCallback; - } + ex => ++errorCallback ); promise.Resolve(expectedValue); @@ -1095,10 +1064,7 @@ public void exception_during_Then_onResolved_triggers_error_hander() promise .Then(value => throw expectedException) .Done( - () => - { - ++callback; - }, + () => ++callback, ex => { Assert.Equal(expectedException, ex); @@ -1129,10 +1095,7 @@ public void finally_is_called_after_resolve() var promise = new Promise(); var callback = 0; - promise.Finally(() => - { - ++callback; - }); + promise.Finally(() => ++callback); promise.Resolve(0); @@ -1145,10 +1108,7 @@ public void finally_is_called_after_reject() var promise = new Promise(); var callback = 0; - promise.Finally(() => - { - ++callback; - }); + promise.Finally(() => ++callback); promise.Reject(new Exception()); @@ -1163,15 +1123,13 @@ public void resolved_chain_continues_after_finally() var callback = 0; const int expectedValue = 42; - promise.Finally(() => - { - ++callback; - }) - .Then((x) => - { - Assert.Equal(expectedValue, x); - ++callback; - }); + promise + .Finally(() => ++callback) + .Then((x) => + { + Assert.Equal(expectedValue, x); + ++callback; + }); promise.Resolve(expectedValue); @@ -1185,14 +1143,9 @@ public void rejected_chain_rejects_after_finally() var promise = new Promise(); var callback = 0; - promise.Finally(() => - { - ++callback; - }) - .Catch(_ => - { - ++callback; - }); + promise + .Finally(() => ++callback) + .Catch(_ => ++callback); promise.Reject(new Exception()); @@ -1205,13 +1158,12 @@ public void rejected_chain_continues_after_ContinueWith_returning_non_value_prom var promise = new Promise(); var callback = 0; - promise.ContinueWith(() => { + promise.ContinueWith(() => + { ++callback; return Promise.Resolved(); }) - .Then(() => { - ++callback; - }); + .Then(() => ++callback); promise.Reject(new Exception()); @@ -1224,11 +1176,13 @@ public void rejected_chain_continues_after_ContinueWith_returning_value_promise( var promise = new Promise(); var callback = 0; const int expectedValue = 42; - promise.ContinueWith(() => { + promise.ContinueWith(() => + { ++callback; return Promise.Resolved(expectedValue); }) - .Then(x => { + .Then(x => + { Assert.Equal(expectedValue, x); ++callback; }); @@ -1268,14 +1222,9 @@ public void can_chain_promise_after_finally() var promise = new Promise(); var callback = 0; - promise.Finally(() => - { - ++callback; - }) - .Then(_ => - { - ++callback; - }); + promise + .Finally(() => ++callback) + .Then(_ => ++callback); promise.Resolve(0); diff --git a/Tests/Promise_NonGeneric_Tests.cs b/Tests/Promise_NonGeneric_Tests.cs index 512c482..d4d9e1c 100644 --- a/Tests/Promise_NonGeneric_Tests.cs +++ b/Tests/Promise_NonGeneric_Tests.cs @@ -91,9 +91,7 @@ public void exception_is_thrown_for_resolve_after_resolve() promise.Resolve(); - Assert.Throws(() => - promise.Resolve() - ); + Assert.Throws(() => promise.Resolve()); } [Fact] @@ -338,10 +336,7 @@ public void combined_promise_is_rejected_when_first_promise_is_rejected() all.Then(() => throw new ApplicationException("Shouldn't happen")); var errors = 0; - all.Catch(e => - { - ++errors; - }); + all.Catch(e => ++errors); promise1.Reject(new ApplicationException("Error!")); promise2.Resolve(); @@ -412,10 +407,7 @@ public void combined_promise_is_resolved_when_all_promises_are_already_resolved( var completed = 0; - all.Then(() => - { - ++completed; - }); + all.Then(() => ++completed); Assert.Equal(1, completed); } @@ -465,7 +457,7 @@ public void can_chain_promise_and_convert_to_promise_that_yields_a_value() { var promise = new Promise(); var chainedPromise = new Promise(); - var chainedPromiseValue = "some-value"; + const string chainedPromiseValue = "some-value"; var completed = 0; @@ -988,10 +980,7 @@ public void inner_exception_handled_by_outer_promise() try { promise - .Then(() => - { - return Promise.Resolved().Then(() => throw expectedException); - }) + .Then(() => Promise.Resolved().Then(() => throw expectedException)) .Catch(ex => { Assert.Equal(expectedException, ex); @@ -1067,10 +1056,7 @@ public void finally_is_called_after_resolve() var promise = new Promise(); var callback = 0; - promise.Finally(() => - { - ++callback; - }); + promise.Finally(() => ++callback); promise.Resolve(); @@ -1083,10 +1069,7 @@ public void finally_is_called_after_reject() var promise = new Promise(); var callback = 0; - promise.Finally(() => - { - ++callback; - }); + promise.Finally(() => ++callback); promise.Reject(new Exception()); @@ -1099,14 +1082,8 @@ public void resolved_chain_continues_after_finally() var promise = new Promise(); var callback = 0; - promise.Finally(() => - { - ++callback; - }) - .Then(() => - { - ++callback; - }); + promise.Finally(() => ++callback) + .Then(() => ++callback); promise.Resolve(); @@ -1119,14 +1096,8 @@ public void rejected_chain_rejects_after_finally() var promise = new Promise(); var callback = 0; - promise.Finally(() => - { - ++callback; - }) - .Catch(_ => - { - ++callback; - }); + promise.Finally(() => ++callback) + .Catch(_ => ++callback); promise.Reject(new Exception()); From b4aefaa27f01b2ff937dbade1d8b073f51036157 Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Wed, 14 Feb 2018 15:21:13 +1000 Subject: [PATCH 56/80] Changed signatures of Then overloads Then with non-value-returning callbacks returns IPromies (non-generic). Then with value-returning callbacks return IPromise. Then with value-returning callback for onResolved must also have a value-returning callback for onRejected. --- Promise.cs | 22 +++++++++++++--------- Promise_NonGeneric.cs | 26 +++++++++++++++++++------- Tests/A+ Spec/2.2.cs | 4 ++-- 3 files changed, 34 insertions(+), 18 deletions(-) diff --git a/Promise.cs b/Promise.cs index f327c1c..fe60f2e 100644 --- a/Promise.cs +++ b/Promise.cs @@ -63,7 +63,7 @@ public interface IPromise /// /// Add a resolved callback. /// - IPromise Then(Action onResolved); + IPromise Then(Action onResolved); /// /// Add a resolved callback and a rejected callback. @@ -83,7 +83,7 @@ Func> onRejected /// /// Add a resolved callback and a rejected callback. /// - IPromise Then(Action onResolved, Action onRejected); + IPromise Then(Action onResolved, Action onRejected); /// /// Add a resolved callback, a rejected callback and a progress callback. @@ -104,7 +104,7 @@ Action onProgress /// /// Add a resolved callback, a rejected callback and a progress callback. /// - IPromise Then(Action onResolved, Action onRejected, Action onProgress); + IPromise Then(Action onResolved, Action onRejected, Action onProgress); /// /// Return a new promise with a different value. @@ -587,7 +587,7 @@ public IPromise Then(Func onResolved) /// /// Add a resolved callback. /// - public IPromise Then(Action onResolved) + public IPromise Then(Action onResolved) { return Then(onResolved, null, null); } @@ -616,7 +616,7 @@ public IPromise Then(Func onResolved, Action onR /// /// Add a resolved callback and a rejected callback. /// - public IPromise Then(Action onResolved, Action onRejected) + public IPromise Then(Action onResolved, Action onRejected) { return Then(onResolved, onRejected, null); } @@ -729,9 +729,9 @@ public IPromise Then(Func onResolved, Action onR /// /// Add a resolved callback, a rejected callback and a progress callback. /// - public IPromise Then(Action onResolved, Action onRejected, Action onProgress) + public IPromise Then(Action onResolved, Action onRejected, Action onProgress) { - var resultPromise = new Promise(); + var resultPromise = new Promise(); resultPromise.WithName(Name); Action resolveHandler = v => @@ -741,7 +741,7 @@ public IPromise Then(Action onResolved, Action onResolved(v); } - resultPromise.Resolve(v); + resultPromise.Resolve(); }; Action rejectHandler = ex => @@ -1000,7 +1000,11 @@ public IPromise Finally(Action onComplete) } }); - return promise.Then(_ => onComplete()); + return promise.Then(v => + { + onComplete(); + return v; + }); } public IPromise ContinueWith(Func onComplete) diff --git a/Promise_NonGeneric.cs b/Promise_NonGeneric.cs index e51011e..0475bdb 100644 --- a/Promise_NonGeneric.cs +++ b/Promise_NonGeneric.cs @@ -64,7 +64,7 @@ public interface IPromise /// Add a resolved callback and a rejected callback. /// The resolved callback chains a value promise (optionally converting to a different value type). /// - IPromise Then(Func> onResolved, Action onRejected); + IPromise Then(Func> onResolved, Func> onRejected); /// /// Add a resolved callback and a rejected callback. @@ -81,7 +81,7 @@ public interface IPromise /// Add a resolved callback, a rejected callback and a progress callback. /// The resolved callback chains a value promise (optionally converting to a different value type). /// - IPromise Then(Func> onResolved, Action onRejected, Action onProgress); + IPromise Then(Func> onResolved, Func> onRejected, Action onProgress); /// /// Add a resolved callback, a rejected callback and a progress callback. @@ -687,7 +687,7 @@ public IPromise Then(Action onResolved) /// Add a resolved callback and a rejected callback. /// The resolved callback chains a value promise (optionally converting to a different value type). /// - public IPromise Then(Func> onResolved, Action onRejected) + public IPromise Then(Func> onResolved, Func> onRejected) { return Then(onResolved, onRejected, null); } @@ -715,7 +715,7 @@ public IPromise Then(Action onResolved, Action onRejected) /// public IPromise Then( Func> onResolved, - Action onRejected, + Func> onRejected, Action onProgress) { // This version of the function must supply an onResolved. @@ -738,12 +738,24 @@ public IPromise Then( Action rejectHandler = ex => { - if (onRejected != null) + if (onRejected == null) { - onRejected(ex); + resultPromise.Reject(ex); + return; } - resultPromise.Reject(ex); + try + { + onRejected(ex) + .Then( + chainedValue => resultPromise.Resolve(chainedValue), + callbackEx => resultPromise.Reject(callbackEx) + ); + } + catch (Exception callbackEx) + { + resultPromise.Reject(callbackEx); + } }; ActionHandlers(resultPromise, resolveHandler, rejectHandler); diff --git a/Tests/A+ Spec/2.2.cs b/Tests/A+ Spec/2.2.cs index f2b6e60..f71e891 100644 --- a/Tests/A+ Spec/2.2.cs +++ b/Tests/A+ Spec/2.2.cs @@ -19,8 +19,8 @@ public void _if_onFulfilled_is_not_a_function_it_must_be_ignored_1() var resultPromise = promise .Then( - (Action)null, - ex => {} + null, + ex => Promise.Resolved(null) ); var resolves = 0; From 5a3020616e3ec464a035474838895a5075a149f3 Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Wed, 14 Feb 2018 17:07:48 +1000 Subject: [PATCH 57/80] Created a custom type of exception and removed ApplicationExceptions --- C-Sharp-Promise.csproj | 2 + Exceptions/PromiseException.cs | 19 ++++++++ Exceptions/PromiseStateException.cs | 22 +++++++++ Promise.cs | 23 +++++++-- Promise_NonGeneric.cs | 21 +++++++-- Tests/A+ Spec/2.1.cs | 11 +++-- Tests/PromiseProgressTests.cs | 7 +-- Tests/PromiseTests.cs | 57 ++++++++++++----------- Tests/Promise_NonGeneric_ProgressTests.cs | 7 +-- Tests/Promise_NonGeneric_Tests.cs | 45 +++++++++--------- 10 files changed, 144 insertions(+), 70 deletions(-) create mode 100644 Exceptions/PromiseException.cs create mode 100644 Exceptions/PromiseStateException.cs diff --git a/C-Sharp-Promise.csproj b/C-Sharp-Promise.csproj index 74594bc..890f3d3 100644 --- a/C-Sharp-Promise.csproj +++ b/C-Sharp-Promise.csproj @@ -51,6 +51,8 @@ + + diff --git a/Exceptions/PromiseException.cs b/Exceptions/PromiseException.cs new file mode 100644 index 0000000..b7e3afc --- /dev/null +++ b/Exceptions/PromiseException.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace RSG.Exceptions +{ + /// + /// Base class for promise exceptions. + /// + public class PromiseException : Exception + { + public PromiseException() { } + + public PromiseException(string message) : base(message) { } + + public PromiseException(string message, Exception inner) : base(message, inner) { } + } +} diff --git a/Exceptions/PromiseStateException.cs b/Exceptions/PromiseStateException.cs new file mode 100644 index 0000000..86741cd --- /dev/null +++ b/Exceptions/PromiseStateException.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace RSG.Exceptions +{ + /// + /// Exception thrown when an operation is performed on a promise that is in an invalid + /// state for it to handle. + /// + public class PromiseStateException : PromiseException + { + public PromiseStateException() { } + + public PromiseStateException(string message) : base(message) { } + + public PromiseStateException(string message, Exception inner) + : base(message, inner) + { } + } +} diff --git a/Promise.cs b/Promise.cs index fe60f2e..00c77d0 100644 --- a/Promise.cs +++ b/Promise.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; +using RSG.Exceptions; namespace RSG { @@ -418,7 +419,11 @@ public void Reject(Exception ex) if (CurState != PromiseState.Pending) { - throw new ApplicationException("Attempt to reject a promise that is already in state: " + CurState + ", a promise can only be rejected when it is still in state: " + PromiseState.Pending); + throw new PromiseStateException( + "Attempt to reject a promise that is already in state: " + CurState + + ", a promise can only be rejected when it is still in state: " + + PromiseState.Pending + ); } rejectionException = ex; @@ -439,7 +444,11 @@ public void Resolve(PromisedT value) { if (CurState != PromiseState.Pending) { - throw new ApplicationException("Attempt to resolve a promise that is already in state: " + CurState + ", a promise can only be resolved when it is still in state: " + PromiseState.Pending); + throw new PromiseStateException( + "Attempt to resolve a promise that is already in state: " + CurState + + ", a promise can only be resolved when it is still in state: " + + PromiseState.Pending + ); } resolveValue = value; @@ -460,7 +469,11 @@ public void ReportProgress(float progress) { if (CurState != PromiseState.Pending) { - throw new ApplicationException("Attempt to report progress on a promise that is already in state: " + CurState + ", a promise can only report progress when it is still in state: " + PromiseState.Pending); + throw new PromiseStateException( + "Attempt to report progress on a promise that is already in state: " + + CurState + ", a promise can only report progress when it is still in state: " + + PromiseState.Pending + ); } InvokeProgressHandlers(progress); @@ -926,7 +939,9 @@ public static IPromise Race(IEnumerable> promises var promisesArray = promises.ToArray(); if (promisesArray.Length == 0) { - throw new ApplicationException("At least 1 input promise must be provided for Race"); + throw new InvalidOperationException( + "At least 1 input promise must be provided for Race" + ); } var resultPromise = new Promise(); diff --git a/Promise_NonGeneric.cs b/Promise_NonGeneric.cs index 0475bdb..8df04fd 100644 --- a/Promise_NonGeneric.cs +++ b/Promise_NonGeneric.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; +using RSG.Exceptions; namespace RSG { @@ -535,7 +536,11 @@ public void Reject(Exception ex) if (CurState != PromiseState.Pending) { - throw new ApplicationException("Attempt to reject a promise that is already in state: " + CurState + ", a promise can only be rejected when it is still in state: " + PromiseState.Pending); + throw new PromiseStateException( + "Attempt to reject a promise that is already in state: " + CurState + + ", a promise can only be rejected when it is still in state: " + + PromiseState.Pending + ); } rejectionException = ex; @@ -557,7 +562,11 @@ public void Resolve() { if (CurState != PromiseState.Pending) { - throw new ApplicationException("Attempt to resolve a promise that is already in state: " + CurState + ", a promise can only be resolved when it is still in state: " + PromiseState.Pending); + throw new PromiseStateException( + "Attempt to resolve a promise that is already in state: " + CurState + + ", a promise can only be resolved when it is still in state: " + + PromiseState.Pending + ); } CurState = PromiseState.Resolved; @@ -578,7 +587,11 @@ public void ReportProgress(float progress) { if (CurState != PromiseState.Pending) { - throw new ApplicationException("Attempt to report progress on a promise that is already in state: " + CurState + ", a promise can only report progress when it is still in state: " + PromiseState.Pending); + throw new PromiseStateException( + "Attempt to report progress on a promise that is already in state: " + + CurState + ", a promise can only report progress when it is still in state: " + + PromiseState.Pending + ); } InvokeProgressHandlers(progress); @@ -1029,7 +1042,7 @@ public static IPromise Race(IEnumerable promises) var promisesArray = promises.ToArray(); if (promisesArray.Length == 0) { - throw new ApplicationException("At least 1 input promise must be provided for Race"); + throw new InvalidOperationException("At least 1 input promise must be provided for Race"); } var resultPromise = new Promise(); diff --git a/Tests/A+ Spec/2.1.cs b/Tests/A+ Spec/2.1.cs index e28d467..88f9552 100644 --- a/Tests/A+ Spec/2.1.cs +++ b/Tests/A+ Spec/2.1.cs @@ -1,7 +1,8 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; +using RSG.Exceptions; using Xunit; namespace RSG.Tests.A__Spec @@ -33,7 +34,7 @@ public void _must_not_transition_to_any_other_state() var fulfilledPromise = new Promise(); fulfilledPromise.Resolve(new object()); - Assert.Throws(() => fulfilledPromise.Reject(new Exception())); + Assert.Throws(() => fulfilledPromise.Reject(new Exception())); Assert.Equal(PromiseState.Resolved, fulfilledPromise.CurState); } @@ -54,7 +55,7 @@ public void _must_have_a_value_which_must_not_change() fulfilledPromise.Resolve(promisedValue); - Assert.Throws(() => fulfilledPromise.Resolve(new object())); + Assert.Throws(() => fulfilledPromise.Resolve(new object())); Assert.Equal(1, handled); } @@ -70,7 +71,7 @@ public void _must_not_transition_to_any_other_state() var rejectedPromise = new Promise(); rejectedPromise.Reject(new Exception()); - Assert.Throws(() => rejectedPromise.Resolve(new object())); + Assert.Throws(() => rejectedPromise.Resolve(new object())); Assert.Equal(PromiseState.Rejected, rejectedPromise.CurState); } @@ -91,7 +92,7 @@ public void _must_have_a_reason_which_must_not_change() rejectedPromise.Reject(reason); - Assert.Throws(() => rejectedPromise.Reject(new Exception())); + Assert.Throws(() => rejectedPromise.Reject(new Exception())); Assert.Equal(1, handled); } diff --git a/Tests/PromiseProgressTests.cs b/Tests/PromiseProgressTests.cs index b2e7e74..c6b5d59 100644 --- a/Tests/PromiseProgressTests.cs +++ b/Tests/PromiseProgressTests.cs @@ -1,4 +1,5 @@ using System; +using RSG.Exceptions; using Xunit; namespace RSG.Tests @@ -141,16 +142,16 @@ public void exception_is_thrown_for_progress_after_resolve() var promise = new Promise(); promise.Resolve(17); - Assert.Throws(() => promise.ReportProgress(1f)); + Assert.Throws(() => promise.ReportProgress(1f)); } [Fact] public void exception_is_thrown_for_progress_after_reject() { var promise = new Promise(); - promise.Reject(new ApplicationException()); + promise.Reject(new Exception()); - Assert.Throws(() => promise.ReportProgress(1f)); + Assert.Throws(() => promise.ReportProgress(1f)); } [Fact] diff --git a/Tests/PromiseTests.cs b/Tests/PromiseTests.cs index 81b6196..75f99a3 100644 --- a/Tests/PromiseTests.cs +++ b/Tests/PromiseTests.cs @@ -1,6 +1,7 @@ using RSG.Promises; using System; using System.Linq; +using RSG.Exceptions; using Xunit; namespace RSG.Tests @@ -44,10 +45,10 @@ public void exception_is_thrown_for_reject_after_reject() { var promise = new Promise(); - promise.Reject(new ApplicationException()); + promise.Reject(new Exception()); - Assert.Throws(() => - promise.Reject(new ApplicationException()) + Assert.Throws(() => + promise.Reject(new Exception()) ); } @@ -58,8 +59,8 @@ public void exception_is_thrown_for_reject_after_resolve() promise.Resolve(5); - Assert.Throws(() => - promise.Reject(new ApplicationException()) + Assert.Throws(() => + promise.Reject(new Exception()) ); } @@ -68,9 +69,9 @@ public void exception_is_thrown_for_resolve_after_reject() { var promise = new Promise(); - promise.Reject(new ApplicationException()); + promise.Reject(new Exception()); - Assert.Throws(() => promise.Resolve(5)); + Assert.Throws(() => promise.Resolve(5)); } [Fact] @@ -99,7 +100,7 @@ public void exception_is_thrown_for_resolve_after_resolve() promise.Resolve(5); - Assert.Throws(() => promise.Resolve(5)); + Assert.Throws(() => promise.Resolve(5)); } [Fact] @@ -141,7 +142,7 @@ public void can_reject_promise_and_trigger_error_handler() { var promise = new Promise(); - var ex = new ApplicationException(); + var ex = new Exception(); var completed = 0; promise.Catch(e => { @@ -159,7 +160,7 @@ public void can_reject_promise_and_trigger_multiple_error_handlers_in_order() { var promise = new Promise(); - var ex = new ApplicationException(); + var ex = new Exception(); var completed = 0; promise.Catch(e => @@ -183,7 +184,7 @@ public void can_reject_promise_and_trigger_error_handler_with_registration_after { var promise = new Promise(); - var ex = new ApplicationException(); + var ex = new Exception(); promise.Reject(ex); var completed = 0; @@ -201,7 +202,7 @@ public void error_handler_is_not_invoked_for_resolved_promised() { var promise = new Promise(); - promise.Catch(e => throw new ApplicationException("This shouldn't happen")); + promise.Catch(e => throw new Exception("This shouldn't happen")); promise.Resolve(5); } @@ -211,9 +212,9 @@ public void then_handler_is_not_invoked_for_rejected_promise() { var promise = new Promise(); - promise.Then(v => throw new ApplicationException("This shouldn't happen")); + promise.Then(v => throw new Exception("This shouldn't happen")); - promise.Reject(new ApplicationException("Rejection!")); + promise.Reject(new Exception("Rejection!")); } [Fact] @@ -436,12 +437,12 @@ public void combined_promise_is_rejected_when_first_promise_is_rejected() var all = Promise.All(EnumerableExt.FromItems>(promise1, promise2)); - all.Then(v => throw new ApplicationException("Shouldn't happen")); + all.Then(v => throw new Exception("Shouldn't happen")); var errors = 0; all.Catch(e => ++errors); - promise1.Reject(new ApplicationException("Error!")); + promise1.Reject(new Exception("Error!")); promise2.Resolve(2); Assert.Equal(1, errors); @@ -455,12 +456,12 @@ public void combined_promise_of_multiple_types_is_rejected_when_first_promise_is var all = PromiseHelpers.All(promise1, promise2); - all.Then(v => throw new ApplicationException("Shouldn't happen")); + all.Then(v => throw new Exception("Shouldn't happen")); var errors = 0; all.Catch(e => ++errors); - promise1.Reject(new ApplicationException("Error!")); + promise1.Reject(new Exception("Error!")); promise2.Resolve(true); Assert.Equal(1, errors); @@ -474,13 +475,13 @@ public void combined_promise_is_rejected_when_second_promise_is_rejected() var all = Promise.All(EnumerableExt.FromItems>(promise1, promise2)); - all.Then(v => throw new ApplicationException("Shouldn't happen")); + all.Then(v => throw new Exception("Shouldn't happen")); var errors = 0; all.Catch(e => ++errors); promise1.Resolve(2); - promise2.Reject(new ApplicationException("Error!")); + promise2.Reject(new Exception("Error!")); Assert.Equal(1, errors); } @@ -493,13 +494,13 @@ public void combined_promise_of_multiple_types_is_rejected_when_second_promise_i var all = PromiseHelpers.All(promise1, promise2); - all.Then(v => throw new ApplicationException("Shouldn't happen")); + all.Then(v => throw new Exception("Shouldn't happen")); var errors = 0; all.Catch(e => ++errors); promise1.Resolve(2); - promise2.Reject(new ApplicationException("Error!")); + promise2.Reject(new Exception("Error!")); Assert.Equal(1, errors); } @@ -512,7 +513,7 @@ public void combined_promise_is_rejected_when_both_promises_are_rejected() var all = Promise.All(EnumerableExt.FromItems>(promise1, promise2)); - all.Then(v => throw new ApplicationException("Shouldn't happen")); + all.Then(v => throw new Exception("Shouldn't happen")); var errors = 0; all.Catch(e => @@ -520,8 +521,8 @@ public void combined_promise_is_rejected_when_both_promises_are_rejected() ++errors; }); - promise1.Reject(new ApplicationException("Error!")); - promise2.Reject(new ApplicationException("Error!")); + promise1.Reject(new Exception("Error!")); + promise2.Reject(new Exception("Error!")); Assert.Equal(1, errors); } @@ -534,13 +535,13 @@ public void combined_promise_of_multiple_types_is_rejected_when_both_promises_ar var all = PromiseHelpers.All(promise1, promise2); - all.Then(v => throw new ApplicationException("Shouldn't happen")); + all.Then(v => throw new Exception("Shouldn't happen")); var errors = 0; all.Catch(e => ++errors); - promise1.Reject(new ApplicationException("Error!")); - promise2.Reject(new ApplicationException("Error!")); + promise1.Reject(new Exception("Error!")); + promise2.Reject(new Exception("Error!")); Assert.Equal(1, errors); } diff --git a/Tests/Promise_NonGeneric_ProgressTests.cs b/Tests/Promise_NonGeneric_ProgressTests.cs index 51ff829..6479695 100644 --- a/Tests/Promise_NonGeneric_ProgressTests.cs +++ b/Tests/Promise_NonGeneric_ProgressTests.cs @@ -1,4 +1,5 @@ using System; +using RSG.Exceptions; using Xunit; namespace RSG.Tests @@ -130,16 +131,16 @@ public void exception_is_thrown_for_progress_after_resolve() var promise = new Promise(); promise.Resolve(); - Assert.Throws(() => promise.ReportProgress(1f)); + Assert.Throws(() => promise.ReportProgress(1f)); } [Fact] public void exception_is_thrown_for_progress_after_reject() { var promise = new Promise(); - promise.Reject(new ApplicationException()); + promise.Reject(new Exception()); - Assert.Throws(() => promise.ReportProgress(1f)); + Assert.Throws(() => promise.ReportProgress(1f)); } [Fact] diff --git a/Tests/Promise_NonGeneric_Tests.cs b/Tests/Promise_NonGeneric_Tests.cs index d4d9e1c..408e46f 100644 --- a/Tests/Promise_NonGeneric_Tests.cs +++ b/Tests/Promise_NonGeneric_Tests.cs @@ -1,6 +1,7 @@ using RSG.Promises; using System; using System.Linq; +using RSG.Exceptions; using Xunit; namespace RSG.Tests @@ -39,10 +40,10 @@ public void exception_is_thrown_for_reject_after_reject() { var promise = new Promise(); - promise.Reject(new ApplicationException()); + promise.Reject(new Exception()); - Assert.Throws(() => - promise.Reject(new ApplicationException()) + Assert.Throws(() => + promise.Reject(new Exception()) ); } @@ -53,8 +54,8 @@ public void exception_is_thrown_for_reject_after_resolve() promise.Resolve(); - Assert.Throws(() => - promise.Reject(new ApplicationException()) + Assert.Throws(() => + promise.Reject(new Exception()) ); } @@ -63,11 +64,9 @@ public void exception_is_thrown_for_resolve_after_reject() { var promise = new Promise(); - promise.Reject(new ApplicationException()); + promise.Reject(new Exception()); - Assert.Throws(() => - promise.Resolve() - ); + Assert.Throws(() => promise.Resolve()); } [Fact] @@ -91,7 +90,7 @@ public void exception_is_thrown_for_resolve_after_resolve() promise.Resolve(); - Assert.Throws(() => promise.Resolve()); + Assert.Throws(() => promise.Resolve()); } [Fact] @@ -128,7 +127,7 @@ public void can_reject_promise_and_trigger_error_handler() { var promise = new Promise(); - var ex = new ApplicationException(); + var ex = new Exception(); var completed = 0; promise.Catch(e => { @@ -146,7 +145,7 @@ public void can_reject_promise_and_trigger_multiple_error_handlers_in_order() { var promise = new Promise(); - var ex = new ApplicationException(); + var ex = new Exception(); var completed = 0; promise.Catch(e => @@ -170,7 +169,7 @@ public void can_reject_promise_and_trigger_error_handler_with_registration_after { var promise = new Promise(); - var ex = new ApplicationException(); + var ex = new Exception(); promise.Reject(ex); var completed = 0; @@ -188,7 +187,7 @@ public void error_handler_is_not_invoked_for_resolved_promised() { var promise = new Promise(); - promise.Catch(e => throw new ApplicationException("This shouldn't happen")); + promise.Catch(e => throw new Exception("This shouldn't happen")); promise.Resolve(); } @@ -198,9 +197,9 @@ public void then_handler_is_not_invoked_for_rejected_promise() { var promise = new Promise(); - promise.Then(() => throw new ApplicationException("This shouldn't happen")); + promise.Then(() => throw new Exception("This shouldn't happen")); - promise.Reject(new ApplicationException("Rejection!")); + promise.Reject(new Exception("Rejection!")); } [Fact] @@ -333,12 +332,12 @@ public void combined_promise_is_rejected_when_first_promise_is_rejected() var all = Promise.All(EnumerableExt.FromItems(promise1, promise2)); - all.Then(() => throw new ApplicationException("Shouldn't happen")); + all.Then(() => throw new Exception("Shouldn't happen")); var errors = 0; all.Catch(e => ++errors); - promise1.Reject(new ApplicationException("Error!")); + promise1.Reject(new Exception("Error!")); promise2.Resolve(); Assert.Equal(1, errors); @@ -352,7 +351,7 @@ public void combined_promise_is_rejected_when_second_promise_is_rejected() var all = Promise.All(EnumerableExt.FromItems(promise1, promise2)); - all.Then(() => throw new ApplicationException("Shouldn't happen")); + all.Then(() => throw new Exception("Shouldn't happen")); var errors = 0; all.Catch(e => @@ -361,7 +360,7 @@ public void combined_promise_is_rejected_when_second_promise_is_rejected() }); promise1.Resolve(); - promise2.Reject(new ApplicationException("Error!")); + promise2.Reject(new Exception("Error!")); Assert.Equal(1, errors); } @@ -374,13 +373,13 @@ public void combined_promise_is_rejected_when_both_promises_are_rejected() var all = Promise.All(EnumerableExt.FromItems(promise1, promise2)); - all.Then(() => throw new ApplicationException("Shouldn't happen")); + all.Then(() => throw new Exception("Shouldn't happen")); var errors = 0; all.Catch(e => ++errors); - promise1.Reject(new ApplicationException("Error!")); - promise2.Reject(new ApplicationException("Error!")); + promise1.Reject(new Exception("Error!")); + promise2.Reject(new Exception("Error!")); Assert.Equal(1, errors); } From 94157c0c7b79d2f03eb7d45a4bcb6488decd8935 Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Thu, 15 Feb 2018 10:07:01 +1000 Subject: [PATCH 58/80] Removed test that is no longer applicable The functionality for optional onResolved callback in Then does not work with Then also converting types. --- Tests/A+ Spec/2.2.cs | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/Tests/A+ Spec/2.2.cs b/Tests/A+ Spec/2.2.cs index f71e891..12bf922 100644 --- a/Tests/A+ Spec/2.2.cs +++ b/Tests/A+ Spec/2.2.cs @@ -13,29 +13,7 @@ public class _both_onFulfilled_and_onRejected_are_optional_arguments_ { // 2.2.1.1 [Fact] - public void _if_onFulfilled_is_not_a_function_it_must_be_ignored_1() - { - var promise = new Promise(); - - var resultPromise = promise - .Then( - null, - ex => Promise.Resolved(null) - ); - - var resolves = 0; - var errors = 0; - resultPromise.Then(_ => ++resolves); - resultPromise.Catch(ex => ++errors); - - promise.Resolve(new object()); - - Assert.Equal(1, resolves); - Assert.Equal(0, errors); - } - - [Fact] - public void _if_onFulfilled_is_not_a_function_it_must_be_ignored_2() + public void _if_onFulfilled_is_not_a_function_it_must_be_ignored() { var promise = new Promise(); From 42f665c0d3ee7d7cee038137f85c56d9f7b78b08 Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Thu, 15 Feb 2018 15:09:05 +1000 Subject: [PATCH 59/80] Fixed issue where Promise.All was triggering unhandled exception handler --- Promise_NonGeneric.cs | 16 +-- Tests/Promise_NonGeneric_Tests.cs | 169 +++++++++++++++++++----------- 2 files changed, 114 insertions(+), 71 deletions(-) diff --git a/Promise_NonGeneric.cs b/Promise_NonGeneric.cs index 8df04fd..4517cfa 100644 --- a/Promise_NonGeneric.cs +++ b/Promise_NonGeneric.cs @@ -948,14 +948,6 @@ public static IPromise All(IEnumerable promises) progress[index] = v; resultPromise.ReportProgress(progress.Average()); }) - .Catch(ex => - { - if (resultPromise.CurState == PromiseState.Pending) - { - // If a promise errorred and the result promise is still pending, reject it. - resultPromise.Reject(ex); - } - }) .Then(() => { progress[index] = 1f; @@ -967,6 +959,14 @@ public static IPromise All(IEnumerable promises) resultPromise.Resolve(); } }) + .Catch(ex => + { + if (resultPromise.CurState == PromiseState.Pending) + { + // If a promise errorred and the result promise is still pending, reject it. + resultPromise.Reject(ex); + } + }) .Done(); }); diff --git a/Tests/Promise_NonGeneric_Tests.cs b/Tests/Promise_NonGeneric_Tests.cs index 408e46f..a7e4b94 100644 --- a/Tests/Promise_NonGeneric_Tests.cs +++ b/Tests/Promise_NonGeneric_Tests.cs @@ -312,16 +312,19 @@ public void combined_promise_is_resolved_when_children_are_resolved() var promise1 = new Promise(); var promise2 = new Promise(); - var all = Promise.All(EnumerableExt.FromItems(promise1, promise2)); + VerifyDoesntThrowUnhandledException(() => + { + var all = Promise.All(EnumerableExt.FromItems(promise1, promise2)); - var completed = 0; + var completed = 0; - all.Then(() => ++completed); + all.Then(() => ++completed); - promise1.Resolve(); - promise2.Resolve(); + promise1.Resolve(); + promise2.Resolve(); - Assert.Equal(1, completed); + Assert.Equal(1, completed); + }); } [Fact] @@ -330,17 +333,18 @@ public void combined_promise_is_rejected_when_first_promise_is_rejected() var promise1 = new Promise(); var promise2 = new Promise(); - var all = Promise.All(EnumerableExt.FromItems(promise1, promise2)); - - all.Then(() => throw new Exception("Shouldn't happen")); + VerifyDoesntThrowUnhandledException(() => + { + var all = Promise.All(EnumerableExt.FromItems(promise1, promise2)); - var errors = 0; - all.Catch(e => ++errors); + var errors = 0; + all.Catch(e => ++errors); - promise1.Reject(new Exception("Error!")); - promise2.Resolve(); + promise1.Reject(new Exception("Error!")); + promise2.Resolve(); - Assert.Equal(1, errors); + Assert.Equal(1, errors); + }); } [Fact] @@ -349,20 +353,17 @@ public void combined_promise_is_rejected_when_second_promise_is_rejected() var promise1 = new Promise(); var promise2 = new Promise(); - var all = Promise.All(EnumerableExt.FromItems(promise1, promise2)); + VerifyDoesntThrowUnhandledException(() => { + var all = Promise.All(EnumerableExt.FromItems(promise1, promise2)); - all.Then(() => throw new Exception("Shouldn't happen")); + var errors = 0; + all.Catch(e => { ++errors; }); - var errors = 0; - all.Catch(e => - { - ++errors; - }); - - promise1.Resolve(); - promise2.Reject(new Exception("Error!")); + promise1.Resolve(); + promise2.Reject(new Exception("Error!")); - Assert.Equal(1, errors); + Assert.Equal(1, errors); + }); } [Fact] @@ -371,29 +372,33 @@ public void combined_promise_is_rejected_when_both_promises_are_rejected() var promise1 = new Promise(); var promise2 = new Promise(); - var all = Promise.All(EnumerableExt.FromItems(promise1, promise2)); - - all.Then(() => throw new Exception("Shouldn't happen")); + VerifyDoesntThrowUnhandledException(() => + { + var all = Promise.All(EnumerableExt.FromItems(promise1, promise2)); - var errors = 0; - all.Catch(e => ++errors); + var errors = 0; + all.Catch(e => ++errors); - promise1.Reject(new Exception("Error!")); - promise2.Reject(new Exception("Error!")); + promise1.Reject(new Exception("Error!")); + promise2.Reject(new Exception("Error!")); - Assert.Equal(1, errors); + Assert.Equal(1, errors); + }); } [Fact] public void combined_promise_is_resolved_if_there_are_no_promises() { - var all = Promise.All(Enumerable.Empty()); + VerifyDoesntThrowUnhandledException(() => + { + var all = Promise.All(Enumerable.Empty()); - var completed = 0; + var completed = 0; - all.Then(() => ++completed); + all.Then(() => ++completed); - Assert.Equal(1, completed); + Assert.Equal(1, completed); + }); } [Fact] @@ -402,13 +407,16 @@ public void combined_promise_is_resolved_when_all_promises_are_already_resolved( var promise1 = Promise.Resolved(); var promise2 = Promise.Resolved(); - var all = Promise.All(EnumerableExt.FromItems(promise1, promise2)); + VerifyDoesntThrowUnhandledException(() => + { + var all = Promise.All(EnumerableExt.FromItems(promise1, promise2)); - var completed = 0; + var completed = 0; - all.Then(() => ++completed); + all.Then(() => ++completed); - Assert.Equal(1, completed); + Assert.Equal(1, completed); + }); } [Fact] @@ -528,13 +536,16 @@ public void race_is_resolved_when_first_promise_is_resolved_first() var completed = 0; - Promise - .Race(promise1, promise2) - .Then(() => ++completed); + VerifyDoesntThrowUnhandledException(() => + { + Promise + .Race(promise1, promise2) + .Then(() => ++completed); - promise1.Resolve(); + promise1.Resolve(); - Assert.Equal(1, completed); + Assert.Equal(1, completed); + }); } [Fact] @@ -545,13 +556,16 @@ public void race_is_resolved_when_second_promise_is_resolved_first() var completed = 0; - Promise - .Race(promise1, promise2) - .Then(() => ++completed); + VerifyDoesntThrowUnhandledException(() => + { + Promise + .Race(promise1, promise2) + .Then(() => ++completed); - promise2.Resolve(); + promise2.Resolve(); - Assert.Equal(1, completed); + Assert.Equal(1, completed); + }); } [Fact] @@ -562,14 +576,17 @@ public void race_is_rejected_when_first_promise_is_rejected_first() Exception ex = null; - Promise - .Race(promise1, promise2) - .Catch(e => ex = e); + VerifyDoesntThrowUnhandledException(() => + { + Promise + .Race(promise1, promise2) + .Catch(e => ex = e); - var expected = new Exception(); - promise1.Reject(expected); + var expected = new Exception(); + promise1.Reject(expected); - Assert.Equal(expected, ex); + Assert.Equal(expected, ex); + }); } [Fact] @@ -580,14 +597,17 @@ public void race_is_rejected_when_second_promise_is_rejected_first() Exception ex = null; - Promise - .Race(promise1, promise2) - .Catch(e => ex = e); + VerifyDoesntThrowUnhandledException(() => + { + Promise + .Race(promise1, promise2) + .Catch(e => ex = e); - var expected = new Exception(); - promise2.Reject(expected); + var expected = new Exception(); + promise2.Reject(expected); - Assert.Equal(expected, ex); + Assert.Equal(expected, ex); + }); } [Fact] @@ -1238,5 +1258,28 @@ public void can_chain_promise_after_ContinueWith() Assert.Equal(2, callback); } + + /// + /// Helper function that checks that the given action doesn't trigger the + /// unhandled exception handler. + /// + private static void VerifyDoesntThrowUnhandledException(Action testAction) + { + Exception unhandledException = null; + EventHandler handler = + (sender, args) => unhandledException = args.Exception; + Promise.UnhandledException += handler; + + try + { + testAction(); + + Assert.Null(unhandledException); + } + finally + { + Promise.UnhandledException -= handler; + } + } } } From 1138594574466c2ee7d31c072ef7a813e454b980 Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Thu, 15 Feb 2018 15:20:12 +1000 Subject: [PATCH 60/80] Made more tests check that the unhandled exception handler is not called --- Tests/Promise_NonGeneric_Tests.cs | 100 +++++++++++++++++------------- 1 file changed, 56 insertions(+), 44 deletions(-) diff --git a/Tests/Promise_NonGeneric_Tests.cs b/Tests/Promise_NonGeneric_Tests.cs index a7e4b94..31aecb0 100644 --- a/Tests/Promise_NonGeneric_Tests.cs +++ b/Tests/Promise_NonGeneric_Tests.cs @@ -211,23 +211,27 @@ public void chain_multiple_promises_using_all() var completed = 0; - promise - .ThenAll(() => EnumerableExt.FromItems(chainedPromise1, chainedPromise2).Cast()) - .Then(() => ++completed); + VerifyDoesntThrowUnhandledException(() => + { + promise + .ThenAll(() => EnumerableExt.FromItems(chainedPromise1, chainedPromise2) + .Cast()) + .Then(() => ++completed); - Assert.Equal(0, completed); + Assert.Equal(0, completed); - promise.Resolve(); + promise.Resolve(); - Assert.Equal(0, completed); + Assert.Equal(0, completed); - chainedPromise1.Resolve(); + chainedPromise1.Resolve(); - Assert.Equal(0, completed); + Assert.Equal(0, completed); - chainedPromise2.Resolve(); + chainedPromise2.Resolve(); - Assert.Equal(1, completed); + Assert.Equal(1, completed); + }); } [Fact] @@ -241,31 +245,35 @@ public void chain_multiple_promises_using_all_that_are_resolved_out_of_order() var completed = 0; - promise - .ThenAll(() => EnumerableExt.FromItems(chainedPromise1, chainedPromise2).Cast>()) - .Then(result => - { - var items = result.ToArray(); - Assert.Equal(2, items.Length); - Assert.Equal(chainedResult1, items[0]); - Assert.Equal(chainedResult2, items[1]); + VerifyDoesntThrowUnhandledException(() => + { + promise + .ThenAll(() => EnumerableExt.FromItems(chainedPromise1, chainedPromise2) + .Cast>()) + .Then(result => + { + var items = result.ToArray(); + Assert.Equal(2, items.Length); + Assert.Equal(chainedResult1, items[0]); + Assert.Equal(chainedResult2, items[1]); - ++completed; - }); + ++completed; + }); - Assert.Equal(0, completed); + Assert.Equal(0, completed); - promise.Resolve(); + promise.Resolve(); - Assert.Equal(0, completed); + Assert.Equal(0, completed); - chainedPromise1.Resolve(chainedResult1); + chainedPromise1.Resolve(chainedResult1); - Assert.Equal(0, completed); + Assert.Equal(0, completed); - chainedPromise2.Resolve(chainedResult2); + chainedPromise2.Resolve(chainedResult2); - Assert.Equal(1, completed); + Assert.Equal(1, completed); + }); } [Fact] @@ -279,31 +287,35 @@ public void chain_multiple_value_promises_using_all_resolved_out_of_order() var completed = 0; - promise - .ThenAll(() => EnumerableExt.FromItems(chainedPromise1, chainedPromise2).Cast>()) - .Then(result => - { - var items = result.ToArray(); - Assert.Equal(2, items.Length); - Assert.Equal(chainedResult1, items[0]); - Assert.Equal(chainedResult2, items[1]); + VerifyDoesntThrowUnhandledException(() => + { + promise + .ThenAll(() => EnumerableExt.FromItems(chainedPromise1, chainedPromise2) + .Cast>()) + .Then(result => + { + var items = result.ToArray(); + Assert.Equal(2, items.Length); + Assert.Equal(chainedResult1, items[0]); + Assert.Equal(chainedResult2, items[1]); - ++completed; - }); + ++completed; + }); - Assert.Equal(0, completed); + Assert.Equal(0, completed); - promise.Resolve(); + promise.Resolve(); - Assert.Equal(0, completed); + Assert.Equal(0, completed); - chainedPromise2.Resolve(chainedResult2); + chainedPromise2.Resolve(chainedResult2); - Assert.Equal(0, completed); + Assert.Equal(0, completed); - chainedPromise1.Resolve(chainedResult1); + chainedPromise1.Resolve(chainedResult1); - Assert.Equal(1, completed); + Assert.Equal(1, completed); + }); } [Fact] From 8f861a8fca0cefea634c120f67cf3848a01de484 Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Thu, 15 Feb 2018 15:20:43 +1000 Subject: [PATCH 61/80] Fixed bug where Promise.All would fail if both promises rejected --- PromiseHelpers.cs | 21 +- Tests/PromiseTests.cs | 459 +++++++++++++++++++++++++----------------- 2 files changed, 290 insertions(+), 190 deletions(-) diff --git a/PromiseHelpers.cs b/PromiseHelpers.cs index 86d90b8..5133232 100644 --- a/PromiseHelpers.cs +++ b/PromiseHelpers.cs @@ -11,6 +11,7 @@ public static IPromise> All(IPromise p1, IPromise var val1 = default(T1); var val2 = default(T2); var numUnresolved = 2; + var numRejected = 0; var promise = new Promise>(); p1 @@ -23,7 +24,15 @@ public static IPromise> All(IPromise p1, IPromise promise.Resolve(Tuple.Create(val1, val2)); } }) - .Catch(e => promise.Reject(e)) + .Catch(e => + { + if (numRejected <= 0) + { + promise.Reject(e); + } + + numRejected++; + }) .Done(); p2 @@ -36,7 +45,15 @@ public static IPromise> All(IPromise p1, IPromise promise.Resolve(Tuple.Create(val1, val2)); } }) - .Catch(e => promise.Reject(e)) + .Catch(e => + { + if (numRejected <= 0) + { + promise.Reject(e); + } + + numRejected++; + }) .Done(); return promise; diff --git a/Tests/PromiseTests.cs b/Tests/PromiseTests.cs index 75f99a3..e364f32 100644 --- a/Tests/PromiseTests.cs +++ b/Tests/PromiseTests.cs @@ -228,31 +228,35 @@ public void chain_multiple_promises_using_all() var completed = 0; - promise - .ThenAll(i => EnumerableExt.FromItems(chainedPromise1, chainedPromise2).Cast>()) - .Then(result => - { - var items = result.ToArray(); - Assert.Equal(2, items.Length); - Assert.Equal(chainedResult1, items[0]); - Assert.Equal(chainedResult2, items[1]); + VerifyDoesntThrowUnhandledException(() => + { + promise + .ThenAll(i => EnumerableExt.FromItems(chainedPromise1, chainedPromise2) + .Cast>()) + .Then(result => + { + var items = result.ToArray(); + Assert.Equal(2, items.Length); + Assert.Equal(chainedResult1, items[0]); + Assert.Equal(chainedResult2, items[1]); - ++completed; - }); + ++completed; + }); - Assert.Equal(0, completed); + Assert.Equal(0, completed); - promise.Resolve("hello"); + promise.Resolve("hello"); - Assert.Equal(0, completed); + Assert.Equal(0, completed); - chainedPromise1.Resolve(chainedResult1); + chainedPromise1.Resolve(chainedResult1); - Assert.Equal(0, completed); + Assert.Equal(0, completed); - chainedPromise2.Resolve(chainedResult2); + chainedPromise2.Resolve(chainedResult2); - Assert.Equal(1, completed); + Assert.Equal(1, completed); + }); } @@ -267,31 +271,35 @@ public void chain_multiple_promises_using_all_that_are_resolved_out_of_order() var completed = 0; - promise - .ThenAll(i => EnumerableExt.FromItems(chainedPromise1, chainedPromise2).Cast>()) - .Then(result => - { - var items = result.ToArray(); - Assert.Equal(2, items.Length); - Assert.Equal(chainedResult1, items[0]); - Assert.Equal(chainedResult2, items[1]); + VerifyDoesntThrowUnhandledException(() => + { + promise + .ThenAll(i => EnumerableExt.FromItems(chainedPromise1, chainedPromise2) + .Cast>()) + .Then(result => + { + var items = result.ToArray(); + Assert.Equal(2, items.Length); + Assert.Equal(chainedResult1, items[0]); + Assert.Equal(chainedResult2, items[1]); - ++completed; - }); + ++completed; + }); - Assert.Equal(0, completed); + Assert.Equal(0, completed); - promise.Resolve("hello"); + promise.Resolve("hello"); - Assert.Equal(0, completed); + Assert.Equal(0, completed); - chainedPromise2.Resolve(chainedResult2); + chainedPromise2.Resolve(chainedResult2); - Assert.Equal(0, completed); + Assert.Equal(0, completed); - chainedPromise1.Resolve(chainedResult1); + chainedPromise1.Resolve(chainedResult1); - Assert.Equal(1, completed); + Assert.Equal(1, completed); + }); } [Fact] @@ -303,23 +311,27 @@ public void chain_multiple_promises_using_all_and_convert_to_non_value_promise() var completed = 0; - promise - .ThenAll(i => EnumerableExt.FromItems(chainedPromise1, chainedPromise2).Cast()) - .Then(() => ++completed); + VerifyDoesntThrowUnhandledException(() => + { + promise + .ThenAll(i => EnumerableExt.FromItems(chainedPromise1, chainedPromise2) + .Cast()) + .Then(() => ++completed); - Assert.Equal(0, completed); + Assert.Equal(0, completed); - promise.Resolve("hello"); + promise.Resolve("hello"); - Assert.Equal(0, completed); + Assert.Equal(0, completed); - chainedPromise1.Resolve(); + chainedPromise1.Resolve(); - Assert.Equal(0, completed); + Assert.Equal(0, completed); - chainedPromise2.Resolve(); + chainedPromise2.Resolve(); - Assert.Equal(1, completed); + Assert.Equal(1, completed); + }); } [Fact] @@ -328,24 +340,27 @@ public void combined_promise_is_resolved_when_children_are_resolved() var promise1 = new Promise(); var promise2 = new Promise(); - var all = Promise.All(EnumerableExt.FromItems>(promise1, promise2)); + VerifyDoesntThrowUnhandledException(() => + { + var all = Promise.All(EnumerableExt.FromItems>(promise1, promise2)); - var completed = 0; + var completed = 0; - all.Then(v => - { - ++completed; + all.Then(v => + { + ++completed; - var values = v.ToArray(); - Assert.Equal(2, values.Length); - Assert.Equal(1, values[0]); - Assert.Equal(2, values[1]); - }); + var values = v.ToArray(); + Assert.Equal(2, values.Length); + Assert.Equal(1, values[0]); + Assert.Equal(2, values[1]); + }); - promise1.Resolve(1); - promise2.Resolve(2); + promise1.Resolve(1); + promise2.Resolve(2); - Assert.Equal(1, completed); + Assert.Equal(1, completed); + }); } [Fact] @@ -354,22 +369,25 @@ public void combined_promise_of_multiple_types_is_resolved_when_children_are_res var promise1 = new Promise(); var promise2 = new Promise(); - var all = PromiseHelpers.All(promise1, promise2); + VerifyDoesntThrowUnhandledException(() => + { + var all = PromiseHelpers.All(promise1, promise2); - var completed = 0; + var completed = 0; - all.Then(v => - { - ++completed; + all.Then(v => + { + ++completed; - Assert.Equal(1, v.Item1); - Assert.Equal(true, v.Item2); - }); + Assert.Equal(1, v.Item1); + Assert.Equal(true, v.Item2); + }); - promise1.Resolve(1); - promise2.Resolve(true); + promise1.Resolve(1); + promise2.Resolve(true); - Assert.Equal(1, completed); + Assert.Equal(1, completed); + }); } [Fact] @@ -379,24 +397,27 @@ public void combined_promise_of_three_types_is_resolved_when_children_are_resolv var promise2 = new Promise(); var promise3 = new Promise(); - var all = PromiseHelpers.All(promise1, promise2, promise3); + VerifyDoesntThrowUnhandledException(() => + { + var all = PromiseHelpers.All(promise1, promise2, promise3); - var completed = 0; + var completed = 0; - all.Then(v => - { - ++completed; + all.Then(v => + { + ++completed; - Assert.Equal(1, v.Item1); - Assert.Equal(true, v.Item2); - Assert.Equal(3.0f, v.Item3); - }); + Assert.Equal(1, v.Item1); + Assert.Equal(true, v.Item2); + Assert.Equal(3.0f, v.Item3); + }); - promise1.Resolve(1); - promise2.Resolve(true); - promise3.Resolve(3.0f); + promise1.Resolve(1); + promise2.Resolve(true); + promise3.Resolve(3.0f); - Assert.Equal(1, completed); + Assert.Equal(1, completed); + }); } [Fact] @@ -407,26 +428,29 @@ public void combined_promise_of_four_types_is_resolved_when_children_are_resolve var promise3 = new Promise(); var promise4 = new Promise(); - var all = PromiseHelpers.All(promise1, promise2, promise3, promise4); + VerifyDoesntThrowUnhandledException(() => + { + var all = PromiseHelpers.All(promise1, promise2, promise3, promise4); - var completed = 0; + var completed = 0; - all.Then(v => - { - ++completed; + all.Then(v => + { + ++completed; - Assert.Equal(1, v.Item1); - Assert.Equal(true, v.Item2); - Assert.Equal(3.0f, v.Item3); - Assert.Equal(4.0, v.Item4); - }); + Assert.Equal(1, v.Item1); + Assert.Equal(true, v.Item2); + Assert.Equal(3.0f, v.Item3); + Assert.Equal(4.0, v.Item4); + }); - promise1.Resolve(1); - promise2.Resolve(true); - promise3.Resolve(3.0f); - promise4.Resolve(4.0); + promise1.Resolve(1); + promise2.Resolve(true); + promise3.Resolve(3.0f); + promise4.Resolve(4.0); - Assert.Equal(1, completed); + Assert.Equal(1, completed); + }); } [Fact] @@ -435,17 +459,20 @@ public void combined_promise_is_rejected_when_first_promise_is_rejected() var promise1 = new Promise(); var promise2 = new Promise(); - var all = Promise.All(EnumerableExt.FromItems>(promise1, promise2)); + VerifyDoesntThrowUnhandledException(() => + { + var all = Promise.All(EnumerableExt.FromItems>(promise1, promise2)); - all.Then(v => throw new Exception("Shouldn't happen")); + all.Then(v => throw new Exception("Shouldn't happen")); - var errors = 0; - all.Catch(e => ++errors); + var errors = 0; + all.Catch(e => ++errors); - promise1.Reject(new Exception("Error!")); - promise2.Resolve(2); + promise1.Reject(new Exception("Error!")); + promise2.Resolve(2); - Assert.Equal(1, errors); + Assert.Equal(1, errors); + }); } [Fact] @@ -454,17 +481,20 @@ public void combined_promise_of_multiple_types_is_rejected_when_first_promise_is var promise1 = new Promise(); var promise2 = new Promise(); - var all = PromiseHelpers.All(promise1, promise2); + VerifyDoesntThrowUnhandledException(() => + { + var all = PromiseHelpers.All(promise1, promise2); - all.Then(v => throw new Exception("Shouldn't happen")); + all.Then(v => throw new Exception("Shouldn't happen")); - var errors = 0; - all.Catch(e => ++errors); + var errors = 0; + all.Catch(e => ++errors); - promise1.Reject(new Exception("Error!")); - promise2.Resolve(true); + promise1.Reject(new Exception("Error!")); + promise2.Resolve(true); - Assert.Equal(1, errors); + Assert.Equal(1, errors); + }); } [Fact] @@ -473,17 +503,20 @@ public void combined_promise_is_rejected_when_second_promise_is_rejected() var promise1 = new Promise(); var promise2 = new Promise(); - var all = Promise.All(EnumerableExt.FromItems>(promise1, promise2)); + VerifyDoesntThrowUnhandledException(() => + { + var all = Promise.All(EnumerableExt.FromItems>(promise1, promise2)); - all.Then(v => throw new Exception("Shouldn't happen")); + all.Then(v => throw new Exception("Shouldn't happen")); - var errors = 0; - all.Catch(e => ++errors); + var errors = 0; + all.Catch(e => ++errors); - promise1.Resolve(2); - promise2.Reject(new Exception("Error!")); + promise1.Resolve(2); + promise2.Reject(new Exception("Error!")); - Assert.Equal(1, errors); + Assert.Equal(1, errors); + }); } [Fact] @@ -492,17 +525,20 @@ public void combined_promise_of_multiple_types_is_rejected_when_second_promise_i var promise1 = new Promise(); var promise2 = new Promise(); - var all = PromiseHelpers.All(promise1, promise2); + VerifyDoesntThrowUnhandledException(() => + { + var all = PromiseHelpers.All(promise1, promise2); - all.Then(v => throw new Exception("Shouldn't happen")); + all.Then(v => throw new Exception("Shouldn't happen")); - var errors = 0; - all.Catch(e => ++errors); + var errors = 0; + all.Catch(e => ++errors); - promise1.Resolve(2); - promise2.Reject(new Exception("Error!")); + promise1.Resolve(2); + promise2.Reject(new Exception("Error!")); - Assert.Equal(1, errors); + Assert.Equal(1, errors); + }); } [Fact] @@ -511,20 +547,20 @@ public void combined_promise_is_rejected_when_both_promises_are_rejected() var promise1 = new Promise(); var promise2 = new Promise(); - var all = Promise.All(EnumerableExt.FromItems>(promise1, promise2)); + VerifyDoesntThrowUnhandledException(() => + { + var all = Promise.All(EnumerableExt.FromItems>(promise1, promise2)); - all.Then(v => throw new Exception("Shouldn't happen")); + all.Then(v => throw new Exception("Shouldn't happen")); - var errors = 0; - all.Catch(e => - { - ++errors; - }); + var errors = 0; + all.Catch(e => { ++errors; }); - promise1.Reject(new Exception("Error!")); - promise2.Reject(new Exception("Error!")); + promise1.Reject(new Exception("Error!")); + promise2.Reject(new Exception("Error!")); - Assert.Equal(1, errors); + Assert.Equal(1, errors); + }); } [Fact] @@ -533,34 +569,40 @@ public void combined_promise_of_multiple_types_is_rejected_when_both_promises_ar var promise1 = new Promise(); var promise2 = new Promise(); - var all = PromiseHelpers.All(promise1, promise2); + VerifyDoesntThrowUnhandledException(() => + { + var all = PromiseHelpers.All(promise1, promise2); - all.Then(v => throw new Exception("Shouldn't happen")); + all.Then(v => throw new Exception("Shouldn't happen")); - var errors = 0; - all.Catch(e => ++errors); + var errors = 0; + all.Catch(e => ++errors); - promise1.Reject(new Exception("Error!")); - promise2.Reject(new Exception("Error!")); + promise1.Reject(new Exception("Error!")); + promise2.Reject(new Exception("Error!")); - Assert.Equal(1, errors); + Assert.Equal(1, errors); + }); } [Fact] public void combined_promise_is_resolved_if_there_are_no_promises() { - var all = Promise.All(Enumerable.Empty>()); + VerifyDoesntThrowUnhandledException(() => + { + var all = Promise.All(Enumerable.Empty>()); - var completed = 0; + var completed = 0; - all.Then(v => - { - ++completed; + all.Then(v => + { + ++completed; - Assert.Empty(v); - }); + Assert.Empty(v); + }); - Assert.Equal(1, completed); + Assert.Equal(1, completed); + }); } [Fact] @@ -569,18 +611,21 @@ public void combined_promise_is_resolved_when_all_promises_are_already_resolved( var promise1 = Promise.Resolved(1); var promise2 = Promise.Resolved(1); - var all = Promise.All(EnumerableExt.FromItems(promise1, promise2)); + VerifyDoesntThrowUnhandledException(() => + { + var all = Promise.All(EnumerableExt.FromItems(promise1, promise2)); - var completed = 0; + var completed = 0; - all.Then(v => - { - ++completed; + all.Then(v => + { + ++completed; - Assert.Empty(v); - }); + Assert.Empty(v); + }); - Assert.Equal(1, completed); + Assert.Equal(1, completed); + }); } [Fact] @@ -589,19 +634,22 @@ public void combined_promise_of_multiple_types_is_resolved_when_all_promises_are var promise1 = Promise.Resolved(1); var promise2 = Promise.Resolved(true); - var all = PromiseHelpers.All(promise1, promise2); + VerifyDoesntThrowUnhandledException(() => + { + var all = PromiseHelpers.All(promise1, promise2); - var completed = 0; + var completed = 0; - all.Then(v => - { - ++completed; + all.Then(v => + { + ++completed; - Assert.Equal(1, v.Item1); - Assert.Equal(true, v.Item2); - }); + Assert.Equal(1, v.Item1); + Assert.Equal(true, v.Item2); + }); - Assert.Equal(1, completed); + Assert.Equal(1, completed); + }); } [Fact] @@ -768,13 +816,16 @@ public void race_is_resolved_when_first_promise_is_resolved_first() var resolved = 0; - Promise - .Race(promise1, promise2) - .Then(i => resolved = i); + VerifyDoesntThrowUnhandledException(() => + { + Promise + .Race(promise1, promise2) + .Then(i => resolved = i); - promise1.Resolve(5); + promise1.Resolve(5); - Assert.Equal(5, resolved); + Assert.Equal(5, resolved); + }); } [Fact] @@ -785,13 +836,16 @@ public void race_is_resolved_when_second_promise_is_resolved_first() var resolved = 0; - Promise - .Race(promise1, promise2) - .Then(i => resolved = i); + VerifyDoesntThrowUnhandledException(() => + { + Promise + .Race(promise1, promise2) + .Then(i => resolved = i); - promise2.Resolve(12); + promise2.Resolve(12); - Assert.Equal(12, resolved); + Assert.Equal(12, resolved); + }); } [Fact] @@ -802,14 +856,17 @@ public void race_is_rejected_when_first_promise_is_rejected_first() Exception ex = null; - Promise - .Race(promise1, promise2) - .Catch(e => ex = e); + VerifyDoesntThrowUnhandledException(() => + { + Promise + .Race(promise1, promise2) + .Catch(e => ex = e); - var expected = new Exception(); - promise1.Reject(expected); + var expected = new Exception(); + promise1.Reject(expected); - Assert.Equal(expected, ex); + Assert.Equal(expected, ex); + }); } [Fact] @@ -820,14 +877,17 @@ public void race_is_rejected_when_second_promise_is_rejected_first() Exception ex = null; - Promise - .Race(promise1, promise2) - .Catch(e => ex = e); + VerifyDoesntThrowUnhandledException(() => + { + Promise + .Race(promise1, promise2) + .Catch(e => ex = e); - var expected = new Exception(); - promise2.Reject(expected); + var expected = new Exception(); + promise2.Reject(expected); - Assert.Equal(expected, ex); + Assert.Equal(expected, ex); + }); } [Fact] @@ -1339,5 +1399,28 @@ public void rejected_reject_callback_is_caught_by_chained_catch() Assert.Equal(expectedException, actualException); } + + /// + /// Helper function that checks that the given action doesn't trigger the + /// unhandled exception handler. + /// + private static void VerifyDoesntThrowUnhandledException(Action testAction) + { + Exception unhandledException = null; + EventHandler handler = + (sender, args) => unhandledException = args.Exception; + Promise.UnhandledException += handler; + + try + { + testAction(); + + Assert.Null(unhandledException); + } + finally + { + Promise.UnhandledException -= handler; + } + } } } From 870869f011209c462329c3ffd008c5e7f22af2da Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Thu, 15 Feb 2018 15:52:17 +1000 Subject: [PATCH 62/80] Refactored test helper out to a shared class --- PromiseHelpers.cs | 10 ++--- Tests/Promise.Tests.csproj | 1 + Tests/PromiseTests.cs | 63 ++++++++++--------------------- Tests/Promise_NonGeneric_Tests.cs | 49 +++++++----------------- Tests/TestHelpers.cs | 31 +++++++++++++++ 5 files changed, 70 insertions(+), 84 deletions(-) create mode 100644 Tests/TestHelpers.cs diff --git a/PromiseHelpers.cs b/PromiseHelpers.cs index 5133232..a545542 100644 --- a/PromiseHelpers.cs +++ b/PromiseHelpers.cs @@ -11,7 +11,7 @@ public static IPromise> All(IPromise p1, IPromise var val1 = default(T1); var val2 = default(T2); var numUnresolved = 2; - var numRejected = 0; + var alreadyRejected = false; var promise = new Promise>(); p1 @@ -26,12 +26,12 @@ public static IPromise> All(IPromise p1, IPromise }) .Catch(e => { - if (numRejected <= 0) + if (!alreadyRejected) { promise.Reject(e); } - numRejected++; + alreadyRejected = true; }) .Done(); @@ -47,12 +47,12 @@ public static IPromise> All(IPromise p1, IPromise }) .Catch(e => { - if (numRejected <= 0) + if (!alreadyRejected) { promise.Reject(e); } - numRejected++; + alreadyRejected = true; }) .Done(); diff --git a/Tests/Promise.Tests.csproj b/Tests/Promise.Tests.csproj index 3f45d9a..13fb647 100644 --- a/Tests/Promise.Tests.csproj +++ b/Tests/Promise.Tests.csproj @@ -58,6 +58,7 @@ + diff --git a/Tests/PromiseTests.cs b/Tests/PromiseTests.cs index e364f32..99c12a7 100644 --- a/Tests/PromiseTests.cs +++ b/Tests/PromiseTests.cs @@ -228,7 +228,7 @@ public void chain_multiple_promises_using_all() var completed = 0; - VerifyDoesntThrowUnhandledException(() => + TestHelpers.VerifyDoesntThrowUnhandledException(() => { promise .ThenAll(i => EnumerableExt.FromItems(chainedPromise1, chainedPromise2) @@ -271,7 +271,7 @@ public void chain_multiple_promises_using_all_that_are_resolved_out_of_order() var completed = 0; - VerifyDoesntThrowUnhandledException(() => + TestHelpers.VerifyDoesntThrowUnhandledException(() => { promise .ThenAll(i => EnumerableExt.FromItems(chainedPromise1, chainedPromise2) @@ -311,7 +311,7 @@ public void chain_multiple_promises_using_all_and_convert_to_non_value_promise() var completed = 0; - VerifyDoesntThrowUnhandledException(() => + TestHelpers.VerifyDoesntThrowUnhandledException(() => { promise .ThenAll(i => EnumerableExt.FromItems(chainedPromise1, chainedPromise2) @@ -340,7 +340,7 @@ public void combined_promise_is_resolved_when_children_are_resolved() var promise1 = new Promise(); var promise2 = new Promise(); - VerifyDoesntThrowUnhandledException(() => + TestHelpers.VerifyDoesntThrowUnhandledException(() => { var all = Promise.All(EnumerableExt.FromItems>(promise1, promise2)); @@ -369,7 +369,7 @@ public void combined_promise_of_multiple_types_is_resolved_when_children_are_res var promise1 = new Promise(); var promise2 = new Promise(); - VerifyDoesntThrowUnhandledException(() => + TestHelpers.VerifyDoesntThrowUnhandledException(() => { var all = PromiseHelpers.All(promise1, promise2); @@ -397,7 +397,7 @@ public void combined_promise_of_three_types_is_resolved_when_children_are_resolv var promise2 = new Promise(); var promise3 = new Promise(); - VerifyDoesntThrowUnhandledException(() => + TestHelpers.VerifyDoesntThrowUnhandledException(() => { var all = PromiseHelpers.All(promise1, promise2, promise3); @@ -428,7 +428,7 @@ public void combined_promise_of_four_types_is_resolved_when_children_are_resolve var promise3 = new Promise(); var promise4 = new Promise(); - VerifyDoesntThrowUnhandledException(() => + TestHelpers.VerifyDoesntThrowUnhandledException(() => { var all = PromiseHelpers.All(promise1, promise2, promise3, promise4); @@ -459,7 +459,7 @@ public void combined_promise_is_rejected_when_first_promise_is_rejected() var promise1 = new Promise(); var promise2 = new Promise(); - VerifyDoesntThrowUnhandledException(() => + TestHelpers.VerifyDoesntThrowUnhandledException(() => { var all = Promise.All(EnumerableExt.FromItems>(promise1, promise2)); @@ -481,7 +481,7 @@ public void combined_promise_of_multiple_types_is_rejected_when_first_promise_is var promise1 = new Promise(); var promise2 = new Promise(); - VerifyDoesntThrowUnhandledException(() => + TestHelpers.VerifyDoesntThrowUnhandledException(() => { var all = PromiseHelpers.All(promise1, promise2); @@ -503,7 +503,7 @@ public void combined_promise_is_rejected_when_second_promise_is_rejected() var promise1 = new Promise(); var promise2 = new Promise(); - VerifyDoesntThrowUnhandledException(() => + TestHelpers.VerifyDoesntThrowUnhandledException(() => { var all = Promise.All(EnumerableExt.FromItems>(promise1, promise2)); @@ -525,7 +525,7 @@ public void combined_promise_of_multiple_types_is_rejected_when_second_promise_i var promise1 = new Promise(); var promise2 = new Promise(); - VerifyDoesntThrowUnhandledException(() => + TestHelpers.VerifyDoesntThrowUnhandledException(() => { var all = PromiseHelpers.All(promise1, promise2); @@ -547,7 +547,7 @@ public void combined_promise_is_rejected_when_both_promises_are_rejected() var promise1 = new Promise(); var promise2 = new Promise(); - VerifyDoesntThrowUnhandledException(() => + TestHelpers.VerifyDoesntThrowUnhandledException(() => { var all = Promise.All(EnumerableExt.FromItems>(promise1, promise2)); @@ -569,7 +569,7 @@ public void combined_promise_of_multiple_types_is_rejected_when_both_promises_ar var promise1 = new Promise(); var promise2 = new Promise(); - VerifyDoesntThrowUnhandledException(() => + TestHelpers.VerifyDoesntThrowUnhandledException(() => { var all = PromiseHelpers.All(promise1, promise2); @@ -588,7 +588,7 @@ public void combined_promise_of_multiple_types_is_rejected_when_both_promises_ar [Fact] public void combined_promise_is_resolved_if_there_are_no_promises() { - VerifyDoesntThrowUnhandledException(() => + TestHelpers.VerifyDoesntThrowUnhandledException(() => { var all = Promise.All(Enumerable.Empty>()); @@ -611,7 +611,7 @@ public void combined_promise_is_resolved_when_all_promises_are_already_resolved( var promise1 = Promise.Resolved(1); var promise2 = Promise.Resolved(1); - VerifyDoesntThrowUnhandledException(() => + TestHelpers.VerifyDoesntThrowUnhandledException(() => { var all = Promise.All(EnumerableExt.FromItems(promise1, promise2)); @@ -634,7 +634,7 @@ public void combined_promise_of_multiple_types_is_resolved_when_all_promises_are var promise1 = Promise.Resolved(1); var promise2 = Promise.Resolved(true); - VerifyDoesntThrowUnhandledException(() => + TestHelpers.VerifyDoesntThrowUnhandledException(() => { var all = PromiseHelpers.All(promise1, promise2); @@ -816,7 +816,7 @@ public void race_is_resolved_when_first_promise_is_resolved_first() var resolved = 0; - VerifyDoesntThrowUnhandledException(() => + TestHelpers.VerifyDoesntThrowUnhandledException(() => { Promise .Race(promise1, promise2) @@ -836,7 +836,7 @@ public void race_is_resolved_when_second_promise_is_resolved_first() var resolved = 0; - VerifyDoesntThrowUnhandledException(() => + TestHelpers.VerifyDoesntThrowUnhandledException(() => { Promise .Race(promise1, promise2) @@ -856,7 +856,7 @@ public void race_is_rejected_when_first_promise_is_rejected_first() Exception ex = null; - VerifyDoesntThrowUnhandledException(() => + TestHelpers.VerifyDoesntThrowUnhandledException(() => { Promise .Race(promise1, promise2) @@ -877,7 +877,7 @@ public void race_is_rejected_when_second_promise_is_rejected_first() Exception ex = null; - VerifyDoesntThrowUnhandledException(() => + TestHelpers.VerifyDoesntThrowUnhandledException(() => { Promise .Race(promise1, promise2) @@ -1399,28 +1399,5 @@ public void rejected_reject_callback_is_caught_by_chained_catch() Assert.Equal(expectedException, actualException); } - - /// - /// Helper function that checks that the given action doesn't trigger the - /// unhandled exception handler. - /// - private static void VerifyDoesntThrowUnhandledException(Action testAction) - { - Exception unhandledException = null; - EventHandler handler = - (sender, args) => unhandledException = args.Exception; - Promise.UnhandledException += handler; - - try - { - testAction(); - - Assert.Null(unhandledException); - } - finally - { - Promise.UnhandledException -= handler; - } - } } } diff --git a/Tests/Promise_NonGeneric_Tests.cs b/Tests/Promise_NonGeneric_Tests.cs index 31aecb0..20b6b81 100644 --- a/Tests/Promise_NonGeneric_Tests.cs +++ b/Tests/Promise_NonGeneric_Tests.cs @@ -211,7 +211,7 @@ public void chain_multiple_promises_using_all() var completed = 0; - VerifyDoesntThrowUnhandledException(() => + TestHelpers.VerifyDoesntThrowUnhandledException(() => { promise .ThenAll(() => EnumerableExt.FromItems(chainedPromise1, chainedPromise2) @@ -245,7 +245,7 @@ public void chain_multiple_promises_using_all_that_are_resolved_out_of_order() var completed = 0; - VerifyDoesntThrowUnhandledException(() => + TestHelpers.VerifyDoesntThrowUnhandledException(() => { promise .ThenAll(() => EnumerableExt.FromItems(chainedPromise1, chainedPromise2) @@ -287,7 +287,7 @@ public void chain_multiple_value_promises_using_all_resolved_out_of_order() var completed = 0; - VerifyDoesntThrowUnhandledException(() => + TestHelpers.VerifyDoesntThrowUnhandledException(() => { promise .ThenAll(() => EnumerableExt.FromItems(chainedPromise1, chainedPromise2) @@ -324,7 +324,7 @@ public void combined_promise_is_resolved_when_children_are_resolved() var promise1 = new Promise(); var promise2 = new Promise(); - VerifyDoesntThrowUnhandledException(() => + TestHelpers.VerifyDoesntThrowUnhandledException(() => { var all = Promise.All(EnumerableExt.FromItems(promise1, promise2)); @@ -345,7 +345,7 @@ public void combined_promise_is_rejected_when_first_promise_is_rejected() var promise1 = new Promise(); var promise2 = new Promise(); - VerifyDoesntThrowUnhandledException(() => + TestHelpers.VerifyDoesntThrowUnhandledException(() => { var all = Promise.All(EnumerableExt.FromItems(promise1, promise2)); @@ -365,7 +365,7 @@ public void combined_promise_is_rejected_when_second_promise_is_rejected() var promise1 = new Promise(); var promise2 = new Promise(); - VerifyDoesntThrowUnhandledException(() => { + TestHelpers.VerifyDoesntThrowUnhandledException(() => { var all = Promise.All(EnumerableExt.FromItems(promise1, promise2)); var errors = 0; @@ -384,7 +384,7 @@ public void combined_promise_is_rejected_when_both_promises_are_rejected() var promise1 = new Promise(); var promise2 = new Promise(); - VerifyDoesntThrowUnhandledException(() => + TestHelpers.VerifyDoesntThrowUnhandledException(() => { var all = Promise.All(EnumerableExt.FromItems(promise1, promise2)); @@ -401,7 +401,7 @@ public void combined_promise_is_rejected_when_both_promises_are_rejected() [Fact] public void combined_promise_is_resolved_if_there_are_no_promises() { - VerifyDoesntThrowUnhandledException(() => + TestHelpers.VerifyDoesntThrowUnhandledException(() => { var all = Promise.All(Enumerable.Empty()); @@ -419,7 +419,7 @@ public void combined_promise_is_resolved_when_all_promises_are_already_resolved( var promise1 = Promise.Resolved(); var promise2 = Promise.Resolved(); - VerifyDoesntThrowUnhandledException(() => + TestHelpers.VerifyDoesntThrowUnhandledException(() => { var all = Promise.All(EnumerableExt.FromItems(promise1, promise2)); @@ -548,7 +548,7 @@ public void race_is_resolved_when_first_promise_is_resolved_first() var completed = 0; - VerifyDoesntThrowUnhandledException(() => + TestHelpers.VerifyDoesntThrowUnhandledException(() => { Promise .Race(promise1, promise2) @@ -568,7 +568,7 @@ public void race_is_resolved_when_second_promise_is_resolved_first() var completed = 0; - VerifyDoesntThrowUnhandledException(() => + TestHelpers.VerifyDoesntThrowUnhandledException(() => { Promise .Race(promise1, promise2) @@ -588,7 +588,7 @@ public void race_is_rejected_when_first_promise_is_rejected_first() Exception ex = null; - VerifyDoesntThrowUnhandledException(() => + TestHelpers.VerifyDoesntThrowUnhandledException(() => { Promise .Race(promise1, promise2) @@ -609,7 +609,7 @@ public void race_is_rejected_when_second_promise_is_rejected_first() Exception ex = null; - VerifyDoesntThrowUnhandledException(() => + TestHelpers.VerifyDoesntThrowUnhandledException(() => { Promise .Race(promise1, promise2) @@ -1270,28 +1270,5 @@ public void can_chain_promise_after_ContinueWith() Assert.Equal(2, callback); } - - /// - /// Helper function that checks that the given action doesn't trigger the - /// unhandled exception handler. - /// - private static void VerifyDoesntThrowUnhandledException(Action testAction) - { - Exception unhandledException = null; - EventHandler handler = - (sender, args) => unhandledException = args.Exception; - Promise.UnhandledException += handler; - - try - { - testAction(); - - Assert.Null(unhandledException); - } - finally - { - Promise.UnhandledException -= handler; - } - } } } diff --git a/Tests/TestHelpers.cs b/Tests/TestHelpers.cs new file mode 100644 index 0000000..b014c1f --- /dev/null +++ b/Tests/TestHelpers.cs @@ -0,0 +1,31 @@ +using System; +using Xunit; + +namespace RSG.Tests +{ + internal static class TestHelpers + { + /// + /// Helper function that checks that the given action doesn't trigger the + /// unhandled exception handler. + /// + internal static void VerifyDoesntThrowUnhandledException(Action testAction) + { + Exception unhandledException = null; + EventHandler handler = + (sender, args) => unhandledException = args.Exception; + Promise.UnhandledException += handler; + + try + { + testAction(); + + Assert.Null(unhandledException); + } + finally + { + Promise.UnhandledException -= handler; + } + } + } +} From 193db9520f12d960502bc3f2eda93fa5405ff110 Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Thu, 15 Feb 2018 16:22:02 +1000 Subject: [PATCH 63/80] Fixed issue where handled rejections would still result in a rejected promise For non-generic promise only, due to test that was not running. --- Promise_NonGeneric.cs | 2 ++ Tests/A+ Spec/2.1.cs | 19 ++++++++---------- Tests/A+ Spec/2.2.cs | 46 +++++++++++-------------------------------- 3 files changed, 22 insertions(+), 45 deletions(-) diff --git a/Promise_NonGeneric.cs b/Promise_NonGeneric.cs index 4517cfa..89045b9 100644 --- a/Promise_NonGeneric.cs +++ b/Promise_NonGeneric.cs @@ -848,6 +848,8 @@ public IPromise Then(Action onResolved, Action onRejected, Action(); Assert.Equal(PromiseState.Pending, pendingPromise1.CurState); @@ -24,7 +21,7 @@ public void When_pending_a_promise_may_transition_to_either_the_fulfilled_or_rej Assert.Equal(PromiseState.Rejected, pendingPromise2.CurState); } - // 2.1.2 + // 2.1.2 public class When_fulfilled_a_promise_ { // 2.1.2.1 @@ -39,7 +36,7 @@ public void _must_not_transition_to_any_other_state() Assert.Equal(PromiseState.Resolved, fulfilledPromise.CurState); } - // 2.1.2.2 + // 2.1.2.2 [Fact] public void _must_have_a_value_which_must_not_change() { @@ -61,10 +58,10 @@ public void _must_have_a_value_which_must_not_change() } } - // 2.1.3 + // 2.1.3 public class When_rejected_a_promise_ { - // 2.1.3.1 + // 2.1.3.1 [Fact] public void _must_not_transition_to_any_other_state() { @@ -77,7 +74,7 @@ public void _must_not_transition_to_any_other_state() } // 2.1.3.21 - [Fact] + [Fact] public void _must_have_a_reason_which_must_not_change() { var rejectedPromise = new Promise(); diff --git a/Tests/A+ Spec/2.2.cs b/Tests/A+ Spec/2.2.cs index 12bf922..cf95514 100644 --- a/Tests/A+ Spec/2.2.cs +++ b/Tests/A+ Spec/2.2.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using Xunit; namespace RSG.Tests.A__Spec @@ -356,6 +353,7 @@ public void _when_promise1_is_rejected_with_no_value_in_catch() Assert.True(callbackInvoked); } + [Fact] public void _when_promise1_is_rejected_with_no_value_in_then() { var callbackInvoked = false; @@ -425,18 +423,12 @@ public void _when_promise1_is_resolved_1() var promise1 = new Promise(); var e = new Exception(); - Func> thenHandler = _ => - { - throw e; - }; + Func> thenHandler = _ => throw e; var promise2 = promise1.Then(thenHandler); - promise1.Catch(_ => - { - throw new Exception("This shouldn't happen!"); - }); + promise1.Catch(_ => throw new Exception("This shouldn't happen!")); var errorHandledForPromise2 = 0; promise2.Catch(ex => @@ -457,18 +449,12 @@ public void _when_promise1_is_resolved_2() var promise1 = new Promise(); var e = new Exception(); - Action thenHandler = _ => - { - throw e; - }; + Action thenHandler = _ => throw e; var promise2 = promise1.Then(thenHandler); - promise1.Catch(_ => - { - throw new Exception("This shouldn't happen!"); - }); + promise1.Catch(_ => throw new Exception("This shouldn't happen!")); var errorHandledForPromise2 = 0; promise2.Catch(ex => @@ -490,15 +476,9 @@ public void _when_promise1_is_rejected() var e = new Exception(); var promise2 = - promise1.Catch(_ => - { - throw e; - }); + promise1.Catch(_ => throw e); - promise1.Catch(_ => - { - throw new Exception("This shouldn't happen!"); - }); + promise1.Catch(_ => throw new Exception("This shouldn't happen!")); var errorHandledForPromise2 = 0; promise2.Catch(ex => @@ -521,9 +501,8 @@ public void _If_onFulfilled_is_not_a_function_and_promise1_is_fulfilled_promise2 var promise1 = new Promise(); var promise2 = promise1.Catch(_ => - { - throw new Exception("There shouldn't be an error"); - }); + throw new Exception("There shouldn't be an error") + ); var promisedValue = new object(); var promise2ThenHandler = 0; @@ -544,10 +523,9 @@ public void _If_onRejected_is_not_a_function_and_promise1_is_rejected_promise2_m { var promise1 = new Promise(); - var promise2 = promise1.Then(_ => - { - throw new Exception("There shouldn't be a then callback"); - }); + var promise2 = promise1.Then(_ => + throw new Exception("There shouldn't be a then callback") + ); var e = new Exception(); var promise2CatchHandler = 0; From a91e16d98577398d5f8cbb5518031a13b2e5155e Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Thu, 15 Feb 2018 16:26:01 +1000 Subject: [PATCH 64/80] Bumped version number to 3.0.0 --- Properties/AssemblyInfo.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs index 398772b..948c451 100644 --- a/Properties/AssemblyInfo.cs +++ b/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.0.0.0")] -[assembly: AssemblyFileVersion("2.0.0.0")] +[assembly: AssemblyVersion("3.0.0")] +[assembly: AssemblyFileVersion("3.0.0")] From ece92db2b022d3d9d61fd27aafc7f2191682278e Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Thu, 15 Feb 2018 16:41:31 +1000 Subject: [PATCH 65/80] Updated recent updates in readme --- README.md | 114 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 60 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index 674ac8a..ec032fa 100644 --- a/README.md +++ b/README.md @@ -5,16 +5,22 @@ title="Promises/A+ 1.0 compliant" align="right" /> -Promises library for C# for management of asynchronous operations. +Promises library for C# for management of asynchronous operations. Inspired by Javascript promises, but slightly different. Used by [Real Serious Games](https://github.com/Real-Serious-Games/C-Sharp-Promise) for building serious games and simulations on Unity3d. -If you are interested in using promises for game development and Unity please see [this article](http://www.what-could-possibly-go-wrong.com/promises-for-game-development/). +If you are interested in using promises for game development and Unity please see [this article](http://www.what-could-possibly-go-wrong.com/promises-for-game-development/). ## Recent Updates +- v3.0 (15 Feburary 2018) + - *Finally* has been modified to work in a way consistent to [Promise.prototype.finally()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/finally) in JavaScript. + - Added support for reporting progress in a promise. + - Signed assembly with a strong name. + - Errors throw custom exception types rather than generic ones. + - Modified some overloads of *Then* that didn't make sense. - v2.0 (4 December 2017) - *Then* functions chained after a *Catch* are now run after the exception is handled rather than being terminated - *Catch* can return a value which will be passed into the next *Then* @@ -27,7 +33,7 @@ If you are interested in using promises for game development and Unity please se - Removed dependency on RSG.Toolkit - v1.2 (8 March 2015) - *Transform* function has been renamed to *Then* (another overload of *Then*). - + ## Projects using this library - **[RestClient for Unity 🤘](https://github.com/proyecto26/RestClient)** @@ -98,10 +104,10 @@ Alternately, to contribute please fork the project in github. Reference the DLL and import the namespace: - using RSG; + using RSG; Create a promise before you start the async operation: - + var promise = new Promise(); The type of the promise should reflect the result of the async op. @@ -141,13 +147,13 @@ To see it in context, here is an example function that downloads text from a URL return promise; // Return the promise so the caller can await resolution (or error). } - + ## Creating a Promise, Alternate Method There is another way to create a promise that replicates the JavaScript convention of passing a *resolver* function into the constructor. The resolver function is passed functions that resolve or reject the promise. This allows you to express the previous example like this: - var promise = new Promise((resolve, reject) => - { + var promise = new Promise((resolve, reject) => + { using (var client = new WebClient()) { client.DownloadStringCompleted += // Monitor event for download completed. @@ -197,7 +203,7 @@ Multiple async operations can be chained one after the other using *Then*: Download("http://www.google.com") .Then(html => - return Download(ExtractFirstLink(html)) // Extract the first link and download it. + return Download(ExtractFirstLink(html)) // Extract the first link and download it. ) .Then(firstLinkHtml => Console.WriteLine(firstLinkHtml) @@ -205,7 +211,7 @@ Multiple async operations can be chained one after the other using *Then*: .Catch(exception => Console.WriteLine("An exception occured while downloading!") ); - + Here we are chaining another download onto the end of the first download. The first link in the html is extracted and we then download that. *Then* expects the return value to be another promise. The chained promise can have a different *result type*. ## Transforming the Results ## @@ -214,7 +220,7 @@ Sometimes you will want to simply transform or modify the resulting value withou Download("http://www.google.com") .Then(html => - return ExtractAllLinks(html)) // Extract all links and return an array of strings. + return ExtractAllLinks(html)) // Extract all links and return an array of strings. ) .Then(links => // The input here is an array of strings. foreach (var link in links) @@ -223,16 +229,16 @@ Sometimes you will want to simply transform or modify the resulting value withou } ); -As is demonstrated the type of the value can also be changed during transformation. In the previous snippet a `Promise` is transformed to a `Promise`. +As is demonstrated the type of the value can also be changed during transformation. In the previous snippet a `Promise` is transformed to a `Promise`. ## Error Handling An error raised in a callback aborts the function and all subsequent callbacks in the chain: - promise.Then(v => Something()) // <--- An error here aborts all subsequent callbacks... + promise.Then(v => Something()) // <--- An error here aborts all subsequent callbacks... .Then(v => SomethingElse()) .Then(v => AnotherThing()) - .Catch(e => HandleError(e)) // <--- Until the error handler is invoked here. + .Catch(e => HandleError(e)) // <--- Until the error handler is invoked here. ## Unhandled Errors @@ -242,10 +248,10 @@ We handle this in a similar way to the JavaScript [Q](http://documentup.com/kris Terminating a Promise chain using `Done`: - promise.Then(v => Something()) + promise.Then(v => Something()) .Then(v => SomethingElse()) .Then(v => AnotherThing()) - .Done(); // <--- Terminate the pipeline and propagate unhandled exceptions. + .Done(); // <--- Terminate the pipeline and propagate unhandled exceptions. To use the `Done` you must apply the following rule: When you get to the end of a chain of promises, you should either return the last promise or end the chain by calling `Done`. @@ -257,7 +263,7 @@ Then forward the exceptions to your own logging system: private void Promise_UnhandledException(object sender, ExceptionEventArgs e) { - Log.Error(e.Exception, "An unhandled promises exception occured!"); + Log.Error(e.Exception, "An unhandled promises exception occured!"); } ## Progress reporting @@ -278,13 +284,13 @@ Listening for progress on a `Then` call: promise .Then(() => promiseB, null, (progress) => Log.Info("promiseA made progress: " + progress)) .Progress(progress => Log.Info("promiseB made progress: " + progress)); - + In order to report progress for a promise, you need to call the `ReportProgress` method: var promise = new Promise(); promise.ReportProgress(0.5f); // Report a 50% completion -## Promises that are already Resolved/Rejected +## Promises that are already Resolved/Rejected For convenience or testing you will at some point need to create a promise that *starts out* in the resolved or rejected state. This is easy to achieve using *Resolved* and *Rejected* functions: @@ -294,16 +300,16 @@ For convenience or testing you will at some point need to create a promise that ## Interfaces ## -The class *Promise* implements the following interfaces: +The class *Promise* implements the following interfaces: - `IPromise` Interface to await promise resolution. -- `IPendingPromise` Interface that can resolve or reject the promise. +- `IPendingPromise` Interface that can resolve or reject the promise. ## Combining Multiple Async Operations ## -The *All* function combines multiple async operations to run in parallel. It converts a collection of promises or a variable length parameter list of promises into a single promise that yields a collection. +The *All* function combines multiple async operations to run in parallel. It converts a collection of promises or a variable length parameter list of promises into a single promise that yields a collection. -Say that each promise yields a value of type *T*, the resulting promise then yields a collection with values of type *T*. +Say that each promise yields a value of type *T*, the resulting promise then yields a collection with values of type *T*. Here is an example that extracts links from multiple pages and merges the results: @@ -335,16 +341,16 @@ The *ThenAll* function is a convenient way of chaining multiple promise onto an promise .Then(result => SomeAsyncOperation(result)) // Chain a single async operation .ThenAll(result => // Chain multiple async operations. - new IPromise[] // Return an enumerable of promises. - { - SomeAsyncOperation1(result), + new IPromise[] // Return an enumerable of promises. + { + SomeAsyncOperation1(result), SomeAsyncOperation2(result), SomeAsyncOperation3(result) } ) - .Done(collection => ...); // Final promise resolves - // with a collection of values - // when all operations have completed. + .Done(collection => ...); // Final promise resolves + // with a collection of values + // when all operations have completed. ## Racing Asynchronous Operations @@ -353,15 +359,15 @@ The *Race* and *ThenRace* functions are similar to the *All* and *ThenAll* funct promise .Then(result => SomeAsyncOperation(result)) // Chain an async operation. .ThenRace(result => // Race multiple async operations. - new IPromise[] // Return an enumerable of promises. - { - SomeAsyncOperation1(result), + new IPromise[] // Return an enumerable of promises. + { + SomeAsyncOperation1(result), SomeAsyncOperation2(result), SomeAsyncOperation3(result) } ) .Done(result => ...); // The result has come from whichever of - // the async operations completed first. + // the async operations completed first. When listening for progress events in a race operation, the progress that you will receive will be the maximum of those reported by all the given promises. @@ -380,13 +386,13 @@ The *Then* function can be used to chain synchronous operations that yield no re What about a promise that has no result? This represents an asynchronous operation that promises only to complete, it doesn't promise to yield any value as a result. I call this a non-value promise, as opposed to a value promise, which is a promise that does yield a value. This might seem like a curiousity but it is actually very useful for sequencing visual effects. -`Promise` is very similar to `Promise` and implements the similar interfaces: `IPromise` and `IPendingPromise`. +`Promise` is very similar to `Promise` and implements the similar interfaces: `IPromise` and `IPendingPromise`. `Promise` functions that affect the resulting value have no relevance for the non-value promise and have been removed. As an example consider the chaining of animation and sound effects as we often need to do in *game development*: - RunAnimation("Foo") // RunAnimation returns a promise that + RunAnimation("Foo") // RunAnimation returns a promise that .Then(() => RunAnimation("Bar")) // is resolved when the animation is complete. .Then(() => PlaySound("AnimComplete")); @@ -396,14 +402,14 @@ From time to time you might want to convert a value promise to a non-value promi As an example consider a recursive link extractor and file downloader function: - public IPromise DownloadAll(string url) + public IPromise DownloadAll(string url) { return DownloadURL(url) // Yields a value, the HTML text downloaded. .Then(html => ExtractLinks(html)) // Convert HTML into an enumerable of links. - .ThenAll(links => // Process each link. + .ThenAll(links => // Process each link. { // Determine links that should be followed, then follow them. - var linksToFollow = links.Where(link => IsLinkToFollow(link)); + var linksToFollow = links.Where(link => IsLinkToFollow(link)); var linksFollowing = linksToFollow.Select(link => DownloadAll(link)); // Determine links that are files to be downloaded, then download them. @@ -411,18 +417,18 @@ As an example consider a recursive link extractor and file downloader function: var linksDownloading = linksToDownload.Select(link => DownloadFile(link)); // Return an enumerable of promises. - // This combines the recursive link following and any files we want to download. + // This combines the recursive link following and any files we want to download. // Because we are returning an enumerable of non-value promises, the resulting - // chained promises is also non-value. + // chained promises is also non-value. return linksToFollow.Concat(linksDownloading); - }); + }); } Usage: DownloadAll("www.somewhere.com") .Done(() => - Console.WriteLine("Recursive download completed."); + Console.WriteLine("Recursive download completed."); ); @@ -430,7 +436,7 @@ Usage: The `Sequence` and `ThenSequence` functions build a single promise that wraps multiple sequential operations that will be invoked one after the other. -Multiple promise-yielding functions are provided as input, these are chained one after the other and wrapped in a single promise that is resolved once the sequence has completed. +Multiple promise-yielding functions are provided as input, these are chained one after the other and wrapped in a single promise that is resolved once the sequence has completed. var sequence = Promise.Sequence( () => RunAnimation("Foo"), @@ -456,9 +462,9 @@ This might be used, for example, to play a variable length collection of animati Unfortunately we find that we have reached the limits of what is possible with C# type inference, hence the use of the ugly cast `(Func)`. -The cast can easily be removed by converting the inner anonymous function to an actual function which I'll call `PrepAnimation`: +The cast can easily be removed by converting the inner anonymous function to an actual function which I'll call `PrepAnimation`: - private Func PrepAnimation(string animName) + private Func PrepAnimation(string animName) { return () => RunAnimation(animName); } @@ -475,7 +481,7 @@ Holy cow... we've just careened into [functional programming](http://en.wikipedi ## Combining Parallel and Sequential Operations -We can easily combine sequential and parallel operations to build very expressive logic. +We can easily combine sequential and parallel operations to build very expressive logic. Promise.Sequence( // Play operations 1 and 2 sequently. () => Promise.All( // Operation 1: Play animation and sound at same time. @@ -484,7 +490,7 @@ We can easily combine sequential and parallel operations to build very expressiv ), () => Promise.All( RunAnimation("One"), // Operation 2: Play animation and sound at same time. - PlaySound("Two") + PlaySound("Two") ) ); @@ -495,14 +501,14 @@ I'm starting to feel like we are defining behavior trees. If you have a promise that comprises a sequence of other promises, you might want to report the total progress for these, and even give more weight to the progress of some promise over another. In this example, we are first downloading an asset from some URL and then we are loading the downloaded asset into memory. We consider that the time it takes to download the asset will be an 80% of the total time, while the time to load it into memory is a 20%: var promise = new Promise(); - + Download(url) .Progress((downloadProgress) => promise.ReportProgress(0.8f * downloadProgress)) .Then((asset) => LoadAssetIntoMemory(asset)) .Progress((loadProgress) => promise.ReportProgress(0.8f + 0.2f * loadProgress)) .Then(() => promise.Resolve()) .Catch((ex) => promise.Reject(ex)); - + return promise; ## PromiseTimer class @@ -521,7 +527,7 @@ To use it, create an instance of the promise timer and call its `Update` method } // Run once for every frame - equivilant to Update() in Unity - void MainLoop() + void MainLoop() { // deltaTime is equal to the time since the last MainLoop promiseTimer.Update(Time.deltaTime); @@ -536,7 +542,7 @@ Note that usually it is best to call `PromiseTimer.Update` *before* your other l This method creates a promise that resolves after the specified amount of time in seconds has passed. Time is calculated as the sum of the delta values passed into `PromiseTimer.Update` - IPromise LogAfterFourSeconds() + IPromise LogAfterFourSeconds() { return promiseTimer.WaitFor(4f) .Then(() => Console.Log("4 seconds have passed!")); @@ -546,9 +552,9 @@ This method creates a promise that resolves after the specified amount of time i WaitUntil takes a predicate to check each update and resolves once the predicate returns true. This predicate function is passed a `TimeData` object, which just contains the most recent frame's `deltaTime` and `elapsedTime` which is the total amount of time since the promise was created. - IPromise FadeOut(float duration) + IPromise FadeOut(float duration) { - return promiseTimer.WaitUntil(timeData => + return promiseTimer.WaitUntil(timeData => { // Here we are using the amount of elapsed time to calculate what the current // fade value should be (between 0 and 1). @@ -582,7 +588,7 @@ TimeData is passed to you as a paramter when using either PromiseTimer.WaitUntil - Example1 - Example of downloading text from a URL using a promise. - Example2 - - Example of a promise that is rejected because of an error during + - Example of a promise that is rejected because of an error during - the async operation. - Example3 - This example downloads search results from google then transforms the result to extract links. From 0c9908a71d563b55562eb96d327e2699907240be Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Thu, 15 Feb 2018 16:42:27 +1000 Subject: [PATCH 66/80] Updated table of contents in readme --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index ec032fa..b963131 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,7 @@ If you are interested in using promises for game development and Unity please se - [Transforming the Results](#transforming-the-results) - [Error Handling](#error-handling) - [Unhandled Errors](#unhandled-errors) +- [Progress reporting](#progress-reporting) - [Promises that are already Resolved/Rejected](#promises-that-are-already-resolvedrejected) - [Interfaces](#interfaces) - [Combining Multiple Async Operations](#combining-multiple-async-operations) @@ -64,10 +65,12 @@ If you are interested in using promises for game development and Unity please se - [Convert a value promise to a non-value promise](#convert-a-value-promise-to-a-non-value-promise) - [Running a Sequence of Operations](#running-a-sequence-of-operations) - [Combining Parallel and Sequential Operations](#combining-parallel-and-sequential-operations) +- [Weighted averaging of progress on multiple promises](#weighted-averaging-of-progress-on-multiple-promises) - [PromiseTimer class](#promisetimer-class) - [PromiseTimer.WaitFor](#promisetimerwaitfor) - [PromiseTimer.WaitUntil](#promisetimerwaituntil) - [PromiseTimer.WaitWhile](#promisetimerwaitwhile) + - [TimeData struct](#timedata-struct) - [Examples](#examples) From 9f49b4bafd7c07435ef14d6893e01292869b28d7 Mon Sep 17 00:00:00 2001 From: Alberto Alonso Date: Sun, 18 Feb 2018 23:04:29 +0200 Subject: [PATCH 67/80] Make Promise.Sequence report progress. --- Promise_NonGeneric.cs | 33 +++++++++++++++++++-- Tests/Promise_NonGeneric_ProgressTests.cs | 36 +++++++++++++++++++++++ 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/Promise_NonGeneric.cs b/Promise_NonGeneric.cs index 89045b9..63c3d8b 100644 --- a/Promise_NonGeneric.cs +++ b/Promise_NonGeneric.cs @@ -1001,10 +1001,37 @@ public static IPromise Sequence(params Func[] fns) /// public static IPromise Sequence(IEnumerable> fns) { - return fns.Aggregate( + var promise = new Promise(); + + int count = 0; + + fns.Aggregate( Resolved(), - (prevPromise, fn) => prevPromise.Then(fn) - ); + (prevPromise, fn) => + { + int itemSequence = count; + ++count; + + return prevPromise + .Then(() => + { + var sliceLength = 1f / count; + promise.ReportProgress(sliceLength * itemSequence); + return fn(); + }) + .Progress(v => + { + var sliceLength = 1f / count; + promise.ReportProgress(sliceLength * (v + itemSequence)); + }) + ; + } + ) + .Then(() => promise.Resolve()) + .Catch(promise.Reject) + .Done(); + + return promise; } /// diff --git a/Tests/Promise_NonGeneric_ProgressTests.cs b/Tests/Promise_NonGeneric_ProgressTests.cs index 6479695..90251e5 100644 --- a/Tests/Promise_NonGeneric_ProgressTests.cs +++ b/Tests/Promise_NonGeneric_ProgressTests.cs @@ -213,5 +213,41 @@ public void all_progress_with_resolved() Assert.Equal(1, reportedCount); } + + [Fact] + public void sequence_reports_progress() + { + var promiseA = new Promise(); + var promiseB = new Promise(); + var promiseC = Promise.Resolved(); + var promiseD = new Promise(); + int currentReport = 0; + var expectedProgress = new[] { 0.125f, 0.25f, 0.25f, 0.3125f, 0.375f, 0.4375f, 0.5f, 0.75f, 0.875f, 1f }; + + Promise + .Sequence(() => promiseA, () => promiseB, () => promiseC, () => promiseD) + .Progress(v => + { + Assert.Equal(expectedProgress[currentReport], v); + ++currentReport; + }) + .Done() + ; + + promiseA.ReportProgress(0.5f); + promiseA.ReportProgress(1f); + promiseA.Resolve(); + + promiseB.ReportProgress(0.25f); + promiseB.ReportProgress(0.5f); + promiseB.ReportProgress(0.75f); + promiseB.Resolve(); + + promiseD.ReportProgress(0.5f); + promiseD.ReportProgress(1f); + promiseD.Resolve(); + + Assert.Equal(expectedProgress.Length, currentReport); + } } } From 388b7dc16347fb354cb17a69a241a3285690cb3c Mon Sep 17 00:00:00 2001 From: Alberto Alonso Date: Mon, 19 Feb 2018 03:06:08 +0200 Subject: [PATCH 68/80] Removed unnecessary call to Done() --- Promise_NonGeneric.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Promise_NonGeneric.cs b/Promise_NonGeneric.cs index 63c3d8b..6f5b2aa 100644 --- a/Promise_NonGeneric.cs +++ b/Promise_NonGeneric.cs @@ -1,4 +1,4 @@ -using RSG.Promises; +using RSG.Promises; using System; using System.Collections.Generic; using System.Linq; @@ -1028,8 +1028,7 @@ public static IPromise Sequence(IEnumerable> fns) } ) .Then(() => promise.Resolve()) - .Catch(promise.Reject) - .Done(); + .Catch(promise.Reject); return promise; } From 17d6af6d1adff6d785a48536c8bd040a8ad346c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sindri=20J=C3=B3elsson?= Date: Tue, 3 Apr 2018 17:23:50 +0200 Subject: [PATCH 69/80] Fix do not report progress on a resolved/rejected promise in Race --- Promise.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Promise.cs b/Promise.cs index 00c77d0..5395223 100644 --- a/Promise.cs +++ b/Promise.cs @@ -954,8 +954,11 @@ public static IPromise Race(IEnumerable> promises promise .Progress(v => { - progress[index] = v; - resultPromise.ReportProgress(progress.Max()); + if (resultPromise.CurState == PromiseState.Pending) + { + progress[index] = v; + resultPromise.ReportProgress(progress.Max()); + } }) .Then(result => { From 04c76b597c9ccf2e26c4cce79950ed713090f104 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Fri, 29 Jun 2018 10:37:26 +0200 Subject: [PATCH 70/80] Syntax highlight --- README.md | 599 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 314 insertions(+), 285 deletions(-) diff --git a/README.md b/README.md index b963131..a7873e8 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Promises library for C# for management of asynchronous operations. -Inspired by Javascript promises, but slightly different. +Inspired by JavaScript promises, but slightly different. Used by [Real Serious Games](https://github.com/Real-Serious-Games/C-Sharp-Promise) for building serious games and simulations on Unity3d. @@ -106,142 +106,150 @@ Alternately, to contribute please fork the project in github. ## Creating a Promise for an Async Operation ## Reference the DLL and import the namespace: - - using RSG; - +```cs +using RSG; +``` Create a promise before you start the async operation: - - var promise = new Promise(); - +```cs +var promise = new Promise(); +``` The type of the promise should reflect the result of the async op. Then initiate your async operation and return the promise to the caller. Upon completion of the async op the promise is resolved: - - promise.Resolve(myValue); - +```cs +promise.Resolve(myValue); +``` The promise is rejected on error/exception: - - promise.Reject(myException); - +```cs +promise.Reject(myException); +``` To see it in context, here is an example function that downloads text from a URL. The promise is resolved when the download completes. If there is an error during download, say *unresolved domain name*, then the promise is rejected: - public IPromise Download(string url) +```cs +public IPromise Download(string url) +{ + var promise = new Promise(); // Create promise. + using (var client = new WebClient()) { - var promise = new Promise(); // Create promise. - using (var client = new WebClient()) - { - client.DownloadStringCompleted += // Monitor event for download completed. - (s, ev) => + client.DownloadStringCompleted += // Monitor event for download completed. + (s, ev) => + { + if (ev.Error != null) { - if (ev.Error != null) - { - promise.Reject(ev.Error); // Error during download, reject the promise. - } - else - { - promise.Resolve(ev.Result); // Downloaded completed successfully, resolve the promise. - } - }; - - client.DownloadStringAsync(new Uri(url), null); // Initiate async op. - } + promise.Reject(ev.Error); // Error during download, reject the promise. + } + else + { + promise.Resolve(ev.Result); // Downloaded completed successfully, resolve the promise. + } + }; - return promise; // Return the promise so the caller can await resolution (or error). + client.DownloadStringAsync(new Uri(url), null); // Initiate async op. } + return promise; // Return the promise so the caller can await resolution (or error). +} +``` + ## Creating a Promise, Alternate Method There is another way to create a promise that replicates the JavaScript convention of passing a *resolver* function into the constructor. The resolver function is passed functions that resolve or reject the promise. This allows you to express the previous example like this: - var promise = new Promise((resolve, reject) => +```cs +var promise = new Promise((resolve, reject) => +{ + using (var client = new WebClient()) { - using (var client = new WebClient()) - { - client.DownloadStringCompleted += // Monitor event for download completed. - (s, ev) => + client.DownloadStringCompleted += // Monitor event for download completed. + (s, ev) => + { + if (ev.Error != null) { - if (ev.Error != null) - { - reject(ev.Error); // Error during download, reject the promise. - } - else - { - resolve(ev.Result); // Downloaded completed successfully, resolve the promise. - } - }; - - client.DownloadStringAsync(new Uri(url), null); // Initiate async op. - } - }); + reject(ev.Error); // Error during download, reject the promise. + } + else + { + resolve(ev.Result); // Downloaded completed successfully, resolve the promise. + } + }; + client.DownloadStringAsync(new Uri(url), null); // Initiate async op. + } +}); +``` ## Waiting for an Async Operation to Complete ## The simplest usage is to register a completion handler to be invoked on completion of the async op: - - Download("http://www.google.com") - .Then(html => - Console.WriteLine(html) - ); +```cs +Download("http://www.google.com") + .Then(html => + Console.WriteLine(html) + ); +``` This snippet downloads the front page from Google and prints it to the console. For all but the most trivial applications you will also want to register an error hander: - - Download("http://www.google.com") - .Then(html => - Console.WriteLine(html) - ) - .Catch(exception => - Console.WriteLine("An exception occured while downloading!") - ); +```cs +Download("http://www.google.com") + .Then(html => + Console.WriteLine(html) + ) + .Catch(exception => + Console.WriteLine("An exception occured while downloading!") + ); +``` The chain of processing for a promise ends as soon as an error/exception occurs. In this case when an error occurs the *Catch* handler would be called, but not the *Done* handler. If there is no error, then only *Done* is called. ## Chaining Async Operations ## Multiple async operations can be chained one after the other using *Then*: - - Download("http://www.google.com") - .Then(html => - return Download(ExtractFirstLink(html)) // Extract the first link and download it. - ) - .Then(firstLinkHtml => - Console.WriteLine(firstLinkHtml) - ) - .Catch(exception => - Console.WriteLine("An exception occured while downloading!") - ); +```cs +Download("http://www.google.com") + .Then(html => + return Download(ExtractFirstLink(html)) // Extract the first link and download it. + ) + .Then(firstLinkHtml => + Console.WriteLine(firstLinkHtml) + ) + .Catch(exception => + Console.WriteLine("An exception occured while downloading!") + ); +``` Here we are chaining another download onto the end of the first download. The first link in the html is extracted and we then download that. *Then* expects the return value to be another promise. The chained promise can have a different *result type*. ## Transforming the Results ## Sometimes you will want to simply transform or modify the resulting value without chaining another async operation. - - Download("http://www.google.com") - .Then(html => - return ExtractAllLinks(html)) // Extract all links and return an array of strings. - ) - .Then(links => // The input here is an array of strings. - foreach (var link in links) - { - Console.WriteLine(link); - } - ); +```cs +Download("http://www.google.com") + .Then(html => + return ExtractAllLinks(html)) // Extract all links and return an array of strings. + ) + .Then(links => // The input here is an array of strings. + foreach (var link in links) + { + Console.WriteLine(link); + } + ); +``` As is demonstrated the type of the value can also be changed during transformation. In the previous snippet a `Promise` is transformed to a `Promise`. ## Error Handling An error raised in a callback aborts the function and all subsequent callbacks in the chain: - - promise.Then(v => Something()) // <--- An error here aborts all subsequent callbacks... - .Then(v => SomethingElse()) - .Then(v => AnotherThing()) - .Catch(e => HandleError(e)) // <--- Until the error handler is invoked here. +```cs +promise.Then(v => Something()) // <--- An error here aborts all subsequent callbacks... + .Then(v => SomethingElse()) + .Then(v => AnotherThing()) + .Catch(e => HandleError(e)) // <--- Until the error handler is invoked here. +``` ## Unhandled Errors @@ -250,24 +258,27 @@ When `Catch` is omitted exceptions go silently unhandled. This is an acknowledge We handle this in a similar way to the JavaScript [Q](http://documentup.com/kriskowal/q) library. The `Done` method is used to terminate a chain, it registers a default catch handler that propagates unhandled exceptions to a default error handling mechanism that can be hooked into by the user. Terminating a Promise chain using `Done`: - - promise.Then(v => Something()) - .Then(v => SomethingElse()) - .Then(v => AnotherThing()) - .Done(); // <--- Terminate the pipeline and propagate unhandled exceptions. +```cs +promise.Then(v => Something()) + .Then(v => SomethingElse()) + .Then(v => AnotherThing()) + .Done(); // <--- Terminate the pipeline and propagate unhandled exceptions. +``` To use the `Done` you must apply the following rule: When you get to the end of a chain of promises, you should either return the last promise or end the chain by calling `Done`. To hook into the unhandled exception stream: - - Promise.UnhandledException += Promise_UnhandledException; +```cs +Promise.UnhandledException += Promise_UnhandledException; +``` Then forward the exceptions to your own logging system: - - private void Promise_UnhandledException(object sender, ExceptionEventArgs e) - { - Log.Error(e.Exception, "An unhandled promises exception occured!"); - } +```cs +private void Promise_UnhandledException(object sender, ExceptionEventArgs e) +{ + Log.Error(e.Exception, "An unhandled promises exception occured!"); +} +``` ## Progress reporting @@ -276,30 +287,34 @@ Promises can additionally report their progress towards completion, allowing the For this, you can either call `Progress` in the promise definition chain or add a third parameter to the `Then` method. Listening for progress reporting from a promise using `Progress`: - - var promise = new Promise(); - promise.Progress((progress) => Log.Info("Current progress is " + (100f * progress) + "%")); +```cs +var promise = new Promise(); +promise.Progress((progress) => Log.Info("Current progress is " + (100f * progress) + "%")); +``` Listening for progress on a `Then` call: - - var promiseA = new Promise(); - var promiseB = new Promise(); - promise - .Then(() => promiseB, null, (progress) => Log.Info("promiseA made progress: " + progress)) - .Progress(progress => Log.Info("promiseB made progress: " + progress)); +```cs +var promiseA = new Promise(); +var promiseB = new Promise(); +promise + .Then(() => promiseB, null, (progress) => Log.Info("promiseA made progress: " + progress)) + .Progress(progress => Log.Info("promiseB made progress: " + progress)); +``` In order to report progress for a promise, you need to call the `ReportProgress` method: - - var promise = new Promise(); - promise.ReportProgress(0.5f); // Report a 50% completion +```cs +var promise = new Promise(); +promise.ReportProgress(0.5f); // Report a 50% completion +``` ## Promises that are already Resolved/Rejected For convenience or testing you will at some point need to create a promise that *starts out* in the resolved or rejected state. This is easy to achieve using *Resolved* and *Rejected* functions: +```cs +var resolvedPromise = Promise.Resolved("some result"); - var resolvedPromise = Promise.Resolved("some result"); - - var rejectedPromise = Promise.Rejected(someException); +var rejectedPromise = Promise.Rejected(someException); +``` ## Interfaces ## @@ -315,75 +330,78 @@ The *All* function combines multiple async operations to run in parallel. It con Say that each promise yields a value of type *T*, the resulting promise then yields a collection with values of type *T*. Here is an example that extracts links from multiple pages and merges the results: - - var urls = new List(); - urls.Add("www.google.com"); - urls.Add("www.yahoo.com"); - - Promise - .All(url => Download(url)) // Download each URL. - .Then(pages => // Receives collection of downloaded pages. - pages.SelectMany( - page => ExtractAllLinks(page) // Extract links from all pages then flatten to single collection of links. - ) +```cs +var urls = new List(); +urls.Add("www.google.com"); +urls.Add("www.yahoo.com"); + +Promise + .All(url => Download(url)) // Download each URL. + .Then(pages => // Receives collection of downloaded pages. + pages.SelectMany( + page => ExtractAllLinks(page) // Extract links from all pages then flatten to single collection of links. ) - .Done(links => // Receives the flattened collection of links from all pages at once. + ) + .Done(links => // Receives the flattened collection of links from all pages at once. + { + foreach (var link in links) { - foreach (var link in links) - { - Console.WriteLine(link); - } - }); + Console.WriteLine(link); + } + }); +``` When listening for progress events in an All operation, the progress that you will receive will be the average of all the progress values reported by all the given promises. ## Chaining Multiple Async Operations The *ThenAll* function is a convenient way of chaining multiple promise onto an existing promise: - - promise - .Then(result => SomeAsyncOperation(result)) // Chain a single async operation - .ThenAll(result => // Chain multiple async operations. - new IPromise[] // Return an enumerable of promises. - { - SomeAsyncOperation1(result), - SomeAsyncOperation2(result), - SomeAsyncOperation3(result) - } - ) - .Done(collection => ...); // Final promise resolves - // with a collection of values - // when all operations have completed. +```cs +promise + .Then(result => SomeAsyncOperation(result)) // Chain a single async operation + .ThenAll(result => // Chain multiple async operations. + new IPromise[] // Return an enumerable of promises. + { + SomeAsyncOperation1(result), + SomeAsyncOperation2(result), + SomeAsyncOperation3(result) + } + ) + .Done(collection => ...); // Final promise resolves + // with a collection of values + // when all operations have completed. +``` ## Racing Asynchronous Operations The *Race* and *ThenRace* functions are similar to the *All* and *ThenAll* functions, but it is the first async operation that completes that wins the race and it's value resolves the promise. - - promise - .Then(result => SomeAsyncOperation(result)) // Chain an async operation. - .ThenRace(result => // Race multiple async operations. - new IPromise[] // Return an enumerable of promises. - { - SomeAsyncOperation1(result), - SomeAsyncOperation2(result), - SomeAsyncOperation3(result) - } - ) - .Done(result => ...); // The result has come from whichever of - // the async operations completed first. +```cs +promise + .Then(result => SomeAsyncOperation(result)) // Chain an async operation. + .ThenRace(result => // Race multiple async operations. + new IPromise[] // Return an enumerable of promises. + { + SomeAsyncOperation1(result), + SomeAsyncOperation2(result), + SomeAsyncOperation3(result) + } + ) + .Done(result => ...); // The result has come from whichever of + // the async operations completed first. +``` When listening for progress events in a race operation, the progress that you will receive will be the maximum of those reported by all the given promises. ## Chaining Synchronous Actions that have no Result The *Then* function can be used to chain synchronous operations that yield no result. - - var promise = ... - promise - .Then(result => SomeAsyncOperation(result)) // Chain an async operation. - .Then(result => Console.WriteLine(result)) // Chain a sync operation that yields no result. - .Done(result => ...); // Result from previous ascync operation skips over the *Do* and is passed through. - +```cs +var promise = ... +promise + .Then(result => SomeAsyncOperation(result)) // Chain an async operation. + .Then(result => Console.WriteLine(result)) // Chain a sync operation that yields no result. + .Done(result => ...); // Result from previous ascync operation skips over the *Do* and is passed through. +``` ## Promises that have no Results (a non-value promise) @@ -394,181 +412,192 @@ What about a promise that has no result? This represents an asynchronous operati `Promise` functions that affect the resulting value have no relevance for the non-value promise and have been removed. As an example consider the chaining of animation and sound effects as we often need to do in *game development*: - - RunAnimation("Foo") // RunAnimation returns a promise that - .Then(() => RunAnimation("Bar")) // is resolved when the animation is complete. - .Then(() => PlaySound("AnimComplete")); +```cs +RunAnimation("Foo") // RunAnimation returns a promise that + .Then(() => RunAnimation("Bar")) // is resolved when the animation is complete. + .Then(() => PlaySound("AnimComplete")); +``` ## Convert a value promise to a non-value promise From time to time you might want to convert a value promise to a non-value promise or vice versa. Both `Promise` and `Promise` have overloads of `Then` and `ThenAll` that do this conversion. You just need to return the appropriate type of promise (for `Then`) or enumerable of promises (for `ThenAll`). As an example consider a recursive link extractor and file downloader function: - - public IPromise DownloadAll(string url) - { - return DownloadURL(url) // Yields a value, the HTML text downloaded. - .Then(html => ExtractLinks(html)) // Convert HTML into an enumerable of links. - .ThenAll(links => // Process each link. - { - // Determine links that should be followed, then follow them. - var linksToFollow = links.Where(link => IsLinkToFollow(link)); - var linksFollowing = linksToFollow.Select(link => DownloadAll(link)); - - // Determine links that are files to be downloaded, then download them. - var linksToDownload = links.Where(link => IsLinkToDownload(link)); - var linksDownloading = linksToDownload.Select(link => DownloadFile(link)); - - // Return an enumerable of promises. - // This combines the recursive link following and any files we want to download. - // Because we are returning an enumerable of non-value promises, the resulting - // chained promises is also non-value. - return linksToFollow.Concat(linksDownloading); - }); - } +```cs +public IPromise DownloadAll(string url) +{ + return DownloadURL(url) // Yields a value, the HTML text downloaded. + .Then(html => ExtractLinks(html)) // Convert HTML into an enumerable of links. + .ThenAll(links => // Process each link. + { + // Determine links that should be followed, then follow them. + var linksToFollow = links.Where(link => IsLinkToFollow(link)); + var linksFollowing = linksToFollow.Select(link => DownloadAll(link)); + + // Determine links that are files to be downloaded, then download them. + var linksToDownload = links.Where(link => IsLinkToDownload(link)); + var linksDownloading = linksToDownload.Select(link => DownloadFile(link)); + + // Return an enumerable of promises. + // This combines the recursive link following and any files we want to download. + // Because we are returning an enumerable of non-value promises, the resulting + // chained promises is also non-value. + return linksToFollow.Concat(linksDownloading); + }); +} +``` Usage: - - DownloadAll("www.somewhere.com") - .Done(() => - Console.WriteLine("Recursive download completed."); - ); - +```cs +DownloadAll("www.somewhere.com") + .Done(() => + Console.WriteLine("Recursive download completed."); + ); +``` ## Running a Sequence of Operations The `Sequence` and `ThenSequence` functions build a single promise that wraps multiple sequential operations that will be invoked one after the other. Multiple promise-yielding functions are provided as input, these are chained one after the other and wrapped in a single promise that is resolved once the sequence has completed. - - var sequence = Promise.Sequence( - () => RunAnimation("Foo"), - () => RunAnimation("Bar"), - () => PlaySound("AnimComplete") - ); +```cs +var sequence = Promise.Sequence( + () => RunAnimation("Foo"), + () => RunAnimation("Bar"), + () => PlaySound("AnimComplete") +); +``` The inputs can also be passed as a collection: - - var operations = ... - var sequence = Promise.Sequence(operations); +```cs +var operations = ... +var sequence = Promise.Sequence(operations); +``` This might be used, for example, to play a variable length collection of animations based on data: - - var animationNames = ... variable length array of animation names loaded from data... - var animations = animationNames.Select(animName => (Func)(() => RunAnimation(animName))); - var sequence = Promise.Sequence(animations); - sequence - .Done(() => - { - // All animations have completed in sequence. - }); +```cs +var animationNames = ... variable length array of animation names loaded from data... +var animations = animationNames.Select(animName => (Func)(() => RunAnimation(animName))); +var sequence = Promise.Sequence(animations); +sequence + .Done(() => + { + // All animations have completed in sequence. + }); +``` Unfortunately we find that we have reached the limits of what is possible with C# type inference, hence the use of the ugly cast `(Func)`. The cast can easily be removed by converting the inner anonymous function to an actual function which I'll call `PrepAnimation`: - - private Func PrepAnimation(string animName) +```cs +private Func PrepAnimation(string animName) +{ + return () => RunAnimation(animName); +} + +var animations = animationNames.Select(animName => PrepAnimation(animName)); +var sequence = Promise.Sequence(animations); +sequence + .Done(() => { - return () => RunAnimation(animName); - } - - var animations = animationNames.Select(animName => PrepAnimation(animName)); - var sequence = Promise.Sequence(animations); - sequence - .Done(() => - { - // All animations have completed in sequence. - }); + // All animations have completed in sequence. + }); +``` Holy cow... we've just careened into [functional programming](http://en.wikipedia.org/wiki/Functional_programming) territory, herein lies very powerful and expressive programming techniques. ## Combining Parallel and Sequential Operations We can easily combine sequential and parallel operations to build very expressive logic. - - Promise.Sequence( // Play operations 1 and 2 sequently. - () => Promise.All( // Operation 1: Play animation and sound at same time. - RunAnimation("Foo"), - PlaySound("Bar") - ), - () => Promise.All( - RunAnimation("One"), // Operation 2: Play animation and sound at same time. - PlaySound("Two") - ) - ); +```cs +Promise.Sequence( // Play operations 1 and 2 sequently. + () => Promise.All( // Operation 1: Play animation and sound at same time. + RunAnimation("Foo"), + PlaySound("Bar") + ), + () => Promise.All( + RunAnimation("One"), // Operation 2: Play animation and sound at same time. + PlaySound("Two") + ) +); +``` I'm starting to feel like we are defining behavior trees. ## Weighted averaging of progress on multiple promises If you have a promise that comprises a sequence of other promises, you might want to report the total progress for these, and even give more weight to the progress of some promise over another. In this example, we are first downloading an asset from some URL and then we are loading the downloaded asset into memory. We consider that the time it takes to download the asset will be an 80% of the total time, while the time to load it into memory is a 20%: +```cs +var promise = new Promise(); - var promise = new Promise(); +Download(url) + .Progress((downloadProgress) => promise.ReportProgress(0.8f * downloadProgress)) + .Then((asset) => LoadAssetIntoMemory(asset)) + .Progress((loadProgress) => promise.ReportProgress(0.8f + 0.2f * loadProgress)) + .Then(() => promise.Resolve()) + .Catch((ex) => promise.Reject(ex)); - Download(url) - .Progress((downloadProgress) => promise.ReportProgress(0.8f * downloadProgress)) - .Then((asset) => LoadAssetIntoMemory(asset)) - .Progress((loadProgress) => promise.ReportProgress(0.8f + 0.2f * loadProgress)) - .Then(() => promise.Resolve()) - .Catch((ex) => promise.Reject(ex)); - - return promise; +return promise; +``` ## PromiseTimer class The promise timer is not part of the Promises/A+ standard but is a utility that makes it possible to create promises that check if a condition is met each time the promise timer is updated. A common usage of this is in games where the promise timer is updated each frame. To use it, create an instance of the promise timer and call its `Update` method in your main loop: +```cs +class Example +{ + private IPromiseTimer promiseTimer; - class Example + Example() { - private IPromiseTimer promiseTimer; - - Example() - { - promiseTimer = new PromiseTimer(); - } + promiseTimer = new PromiseTimer(); + } - // Run once for every frame - equivilant to Update() in Unity - void MainLoop() - { - // deltaTime is equal to the time since the last MainLoop - promiseTimer.Update(Time.deltaTime); + // Run once for every frame - equivilant to Update() in Unity + void MainLoop() + { + // deltaTime is equal to the time since the last MainLoop + promiseTimer.Update(Time.deltaTime); - // Process your other logic here - } + // Process your other logic here } +} +``` Note that usually it is best to call `PromiseTimer.Update` *before* your other logic, otherwise you may have unintended behaviour such as promises that are supposed to take a very short time resolving in the same update loop as they were created in. ### PromiseTimer.WaitFor This method creates a promise that resolves after the specified amount of time in seconds has passed. Time is calculated as the sum of the delta values passed into `PromiseTimer.Update` - - IPromise LogAfterFourSeconds() - { - return promiseTimer.WaitFor(4f) - .Then(() => Console.Log("4 seconds have passed!")); - } +```cs +IPromise LogAfterFourSeconds() +{ + return promiseTimer.WaitFor(4f) + .Then(() => Console.Log("4 seconds have passed!")); +} +``` ### PromiseTimer.WaitUntil WaitUntil takes a predicate to check each update and resolves once the predicate returns true. This predicate function is passed a `TimeData` object, which just contains the most recent frame's `deltaTime` and `elapsedTime` which is the total amount of time since the promise was created. - - IPromise FadeOut(float duration) +```cs +IPromise FadeOut(float duration) +{ + return promiseTimer.WaitUntil(timeData => { - return promiseTimer.WaitUntil(timeData => - { - // Here we are using the amount of elapsed time to calculate what the current - // fade value should be (between 0 and 1). - // Since we're fading our we should be going from 0 (not faded) to 1 (full) - var fadeAmount = Mathf.Clamp01(timeData.elapsedTime / duration); - SetFadeValue(fadeAmount); - - // Resolve the promsie once we've finished. - return fadeAmount >= 1f; - }); - } + // Here we are using the amount of elapsed time to calculate what the current + // fade value should be (between 0 and 1). + // Since we're fading our we should be going from 0 (not faded) to 1 (full) + var fadeAmount = Mathf.Clamp01(timeData.elapsedTime / duration); + SetFadeValue(fadeAmount); + + // Resolve the promsie once we've finished. + return fadeAmount >= 1f; + }); +} +``` ### PromiseTimer.WaitWhile @@ -600,4 +629,4 @@ TimeData is passed to you as a paramter when using either PromiseTimer.WaitUntil - This example downloads search results from google, extracts the links and follows only a single first link, downloads its then prints the result. - Includes both error handling and a completion handler. - Example5 - - This example downloads search results from google, extracts the links, follows all (absolute) links and combines all async operations in a single operation using the All function. + - This example downloads search results from google, extracts the links, follows all (absolute) links and combines all async operations in a single operation using the `All` function. From 8ce73356e08cdbf990e2c579042e8d60c6bfaeaf Mon Sep 17 00:00:00 2001 From: Konstantin Khitrykh Date: Wed, 18 Jul 2018 09:50:00 +0700 Subject: [PATCH 71/80] Fix possible misleading code example; --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a7873e8..587a614 100644 --- a/README.md +++ b/README.md @@ -336,7 +336,7 @@ urls.Add("www.google.com"); urls.Add("www.yahoo.com"); Promise - .All(url => Download(url)) // Download each URL. + .All(urls.Select(url => Download(url))) // Download each URL. .Then(pages => // Receives collection of downloaded pages. pages.SelectMany( page => ExtractAllLinks(page) // Extract links from all pages then flatten to single collection of links. From c8af5558b4c822a6ae5cd8a8ebbde9f636d98cf6 Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Mon, 23 Jul 2018 00:02:54 +1000 Subject: [PATCH 72/80] Move source files to their own folder --- C-Sharp-Promise.sln | 2 +- .../C-Sharp-Promise.csproj | 0 C-Sharp-Promise.snk => src/C-Sharp-Promise.snk | Bin EnumerableExt.cs => src/EnumerableExt.cs | 0 {Exceptions => src/Exceptions}/PromiseException.cs | 0 .../Exceptions}/PromiseStateException.cs | 0 Promise.cs => src/Promise.cs | 0 PromiseHelpers.cs => src/PromiseHelpers.cs | 0 PromiseTimer.cs => src/PromiseTimer.cs | 0 Promise_NonGeneric.cs => src/Promise_NonGeneric.cs | 0 {Properties => src/Properties}/AssemblyInfo.cs | 0 Tuple.cs => src/Tuple.cs | 0 12 files changed, 1 insertion(+), 1 deletion(-) rename C-Sharp-Promise.csproj => src/C-Sharp-Promise.csproj (100%) rename C-Sharp-Promise.snk => src/C-Sharp-Promise.snk (100%) rename EnumerableExt.cs => src/EnumerableExt.cs (100%) rename {Exceptions => src/Exceptions}/PromiseException.cs (100%) rename {Exceptions => src/Exceptions}/PromiseStateException.cs (100%) rename Promise.cs => src/Promise.cs (100%) rename PromiseHelpers.cs => src/PromiseHelpers.cs (100%) rename PromiseTimer.cs => src/PromiseTimer.cs (100%) rename Promise_NonGeneric.cs => src/Promise_NonGeneric.cs (100%) rename {Properties => src/Properties}/AssemblyInfo.cs (100%) rename Tuple.cs => src/Tuple.cs (100%) diff --git a/C-Sharp-Promise.sln b/C-Sharp-Promise.sln index ec9253a..a2e8213 100644 --- a/C-Sharp-Promise.sln +++ b/C-Sharp-Promise.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0.31101.0 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "C-Sharp-Promise", "C-Sharp-Promise.csproj", "{7E1C6A8A-84E7-43C3-AE73-C6E8A9F11AF3}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "C-Sharp-Promise", "src\C-Sharp-Promise.csproj", "{7E1C6A8A-84E7-43C3-AE73-C6E8A9F11AF3}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Promise.Tests", "Tests\Promise.Tests.csproj", "{4B4EE1A6-25A3-43D0-BAE9-5E45E31F2816}" EndProject diff --git a/C-Sharp-Promise.csproj b/src/C-Sharp-Promise.csproj similarity index 100% rename from C-Sharp-Promise.csproj rename to src/C-Sharp-Promise.csproj diff --git a/C-Sharp-Promise.snk b/src/C-Sharp-Promise.snk similarity index 100% rename from C-Sharp-Promise.snk rename to src/C-Sharp-Promise.snk diff --git a/EnumerableExt.cs b/src/EnumerableExt.cs similarity index 100% rename from EnumerableExt.cs rename to src/EnumerableExt.cs diff --git a/Exceptions/PromiseException.cs b/src/Exceptions/PromiseException.cs similarity index 100% rename from Exceptions/PromiseException.cs rename to src/Exceptions/PromiseException.cs diff --git a/Exceptions/PromiseStateException.cs b/src/Exceptions/PromiseStateException.cs similarity index 100% rename from Exceptions/PromiseStateException.cs rename to src/Exceptions/PromiseStateException.cs diff --git a/Promise.cs b/src/Promise.cs similarity index 100% rename from Promise.cs rename to src/Promise.cs diff --git a/PromiseHelpers.cs b/src/PromiseHelpers.cs similarity index 100% rename from PromiseHelpers.cs rename to src/PromiseHelpers.cs diff --git a/PromiseTimer.cs b/src/PromiseTimer.cs similarity index 100% rename from PromiseTimer.cs rename to src/PromiseTimer.cs diff --git a/Promise_NonGeneric.cs b/src/Promise_NonGeneric.cs similarity index 100% rename from Promise_NonGeneric.cs rename to src/Promise_NonGeneric.cs diff --git a/Properties/AssemblyInfo.cs b/src/Properties/AssemblyInfo.cs similarity index 100% rename from Properties/AssemblyInfo.cs rename to src/Properties/AssemblyInfo.cs diff --git a/Tuple.cs b/src/Tuple.cs similarity index 100% rename from Tuple.cs rename to src/Tuple.cs From efb3e03dd31d840f6eebff5c10be8c2a2376b7f8 Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Mon, 23 Jul 2018 00:22:30 +1000 Subject: [PATCH 73/80] Support building for both .NET 3.5 and .NET Standard 2.0 --- src/C-Sharp-Promise.csproj | 86 +++++----------------------------- src/Properties/AssemblyInfo.cs | 36 -------------- 2 files changed, 12 insertions(+), 110 deletions(-) delete mode 100644 src/Properties/AssemblyInfo.cs diff --git a/src/C-Sharp-Promise.csproj b/src/C-Sharp-Promise.csproj index 890f3d3..ad61c66 100644 --- a/src/C-Sharp-Promise.csproj +++ b/src/C-Sharp-Promise.csproj @@ -1,75 +1,13 @@ - - - + - Debug - AnyCPU - {7E1C6A8A-84E7-43C3-AE73-C6E8A9F11AF3} - Library - Properties - RSG - RSG.Promise - v3.5 - 512 - - .\ - true - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - 3 - - - C-Sharp-Promise.snk - - - true - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - false - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + net35;netstandard2.0 + 3.0.1 + Real Serious Games + Promises library for C# for management of asynchronous operations. + Copyright © Real Serious Games 2018 + https://opensource.org/licenses/MIT + https://github.com/Real-Serious-Games/C-Sharp-Promise + + true + + diff --git a/src/Properties/AssemblyInfo.cs b/src/Properties/AssemblyInfo.cs deleted file mode 100644 index 948c451..0000000 --- a/src/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("C-Sharp-Promise")] -[assembly: AssemblyDescription("Simple C# Promises Library")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Real Serious Games")] -[assembly: AssemblyProduct("C-Sharp-Promise")] -[assembly: AssemblyCopyright("Copyright © Real Serious Games 2017")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("ae8d06ab-3808-4635-9fa1-3dfbd87eeeb4")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("3.0.0")] -[assembly: AssemblyFileVersion("3.0.0")] From c7ff10bfdf25683f82115c99609cca626929af85 Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Mon, 23 Jul 2018 00:27:28 +1000 Subject: [PATCH 74/80] Fix assembly signing --- src/C-Sharp-Promise.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/C-Sharp-Promise.csproj b/src/C-Sharp-Promise.csproj index ad61c66..d04df8f 100644 --- a/src/C-Sharp-Promise.csproj +++ b/src/C-Sharp-Promise.csproj @@ -9,5 +9,7 @@ https://github.com/Real-Serious-Games/C-Sharp-Promise true + true + C-Sharp-Promise.snk From d367e8f95e0164f32512dcee744c7fd274680a34 Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Mon, 23 Jul 2018 00:27:43 +1000 Subject: [PATCH 75/80] Update references after moving csproj --- Examples/Example1/Example1.csproj | 2 +- Examples/Example2/Example2.csproj | 2 +- Examples/Example3/Example3.csproj | 2 +- Examples/Example4/Example4.csproj | 2 +- Examples/Example5/Example5.csproj | 2 +- Tests/Promise.Tests.csproj | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Examples/Example1/Example1.csproj b/Examples/Example1/Example1.csproj index 424c3ed..ba99ed8 100644 --- a/Examples/Example1/Example1.csproj +++ b/Examples/Example1/Example1.csproj @@ -50,7 +50,7 @@ - + {7e1c6a8a-84e7-43c3-ae73-c6e8a9f11af3} C-Sharp-Promise diff --git a/Examples/Example2/Example2.csproj b/Examples/Example2/Example2.csproj index da8be8b..3bce523 100644 --- a/Examples/Example2/Example2.csproj +++ b/Examples/Example2/Example2.csproj @@ -50,7 +50,7 @@ - + {7e1c6a8a-84e7-43c3-ae73-c6e8a9f11af3} C-Sharp-Promise diff --git a/Examples/Example3/Example3.csproj b/Examples/Example3/Example3.csproj index da8885f..d2ad462 100644 --- a/Examples/Example3/Example3.csproj +++ b/Examples/Example3/Example3.csproj @@ -50,7 +50,7 @@ - + {7e1c6a8a-84e7-43c3-ae73-c6e8a9f11af3} C-Sharp-Promise diff --git a/Examples/Example4/Example4.csproj b/Examples/Example4/Example4.csproj index 3eecaf6..21d9390 100644 --- a/Examples/Example4/Example4.csproj +++ b/Examples/Example4/Example4.csproj @@ -50,7 +50,7 @@ - + {7e1c6a8a-84e7-43c3-ae73-c6e8a9f11af3} C-Sharp-Promise diff --git a/Examples/Example5/Example5.csproj b/Examples/Example5/Example5.csproj index 05a6349..761f647 100644 --- a/Examples/Example5/Example5.csproj +++ b/Examples/Example5/Example5.csproj @@ -50,7 +50,7 @@ - + {7e1c6a8a-84e7-43c3-ae73-c6e8a9f11af3} C-Sharp-Promise diff --git a/Tests/Promise.Tests.csproj b/Tests/Promise.Tests.csproj index 13fb647..7fb6c87 100644 --- a/Tests/Promise.Tests.csproj +++ b/Tests/Promise.Tests.csproj @@ -61,7 +61,7 @@ - + {7e1c6a8a-84e7-43c3-ae73-c6e8a9f11af3} C-Sharp-Promise From 4359ba357d135d3c8de8efb09a1b70f90b09b479 Mon Sep 17 00:00:00 2001 From: Alberto Alonso Date: Mon, 23 Jul 2018 01:58:36 +0300 Subject: [PATCH 76/80] Fixed Promise.All trying to still report progress or resolve the result promise when one of the given promises has already reported rejection. --- Promise.cs | 7 +++++-- Promise_NonGeneric.cs | 9 ++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Promise.cs b/Promise.cs index 5395223..7647358 100644 --- a/Promise.cs +++ b/Promise.cs @@ -872,7 +872,10 @@ public static IPromise> All(IEnumerable { progress[index] = v; - resultPromise.ReportProgress(progress.Average()); + if (resultPromise.CurState == PromiseState.Pending) + { + resultPromise.ReportProgress(progress.Average()); + } }) .Then(result => { @@ -880,7 +883,7 @@ public static IPromise> All(IEnumerable promises) .Progress(v => { progress[index] = v; - resultPromise.ReportProgress(progress.Average()); + if (resultPromise.CurState == PromiseState.Pending) + { + resultPromise.ReportProgress(progress.Average()); + } }) .Then(() => { progress[index] = 1f; --remainingCount; - if (remainingCount <= 0) + if (remainingCount <= 0 && resultPromise.CurState == PromiseState.Pending) { // This will never happen if any of the promises errorred. resultPromise.Resolve(); From 3da255bfb2235e7334bffbb3d29b764b12849738 Mon Sep 17 00:00:00 2001 From: Alberto Alonso Date: Mon, 23 Jul 2018 02:19:37 +0300 Subject: [PATCH 77/80] Added test cases to prevent regression on the Promise.All bug. --- Tests/PromiseTests.cs | 25 +++++++++++++++++++++++++ Tests/Promise_NonGeneric_Tests.cs | 25 +++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/Tests/PromiseTests.cs b/Tests/PromiseTests.cs index 99c12a7..7d0c3f6 100644 --- a/Tests/PromiseTests.cs +++ b/Tests/PromiseTests.cs @@ -652,6 +652,31 @@ public void combined_promise_of_multiple_types_is_resolved_when_all_promises_are }); } + [Fact] + public void all_with_rejected_promise() + { + bool resolved = false; + bool rejected = false; + Exception caughtException = null; + Exception exception = new Exception(); + + var promiseA = new Promise(); + var promise = Promise + .All(promiseA, Promise.Rejected(exception)) + .Then(values => resolved = true) + .Catch(ex => + { + caughtException = ex; + rejected = true; + }); + promiseA.ReportProgress(0.5f); + promiseA.Resolve(0); + + Assert.Equal(false, resolved); + Assert.Equal(true, rejected); + Assert.Equal(exception, caughtException); + } + [Fact] public void can_transform_promise_value() { diff --git a/Tests/Promise_NonGeneric_Tests.cs b/Tests/Promise_NonGeneric_Tests.cs index 20b6b81..7cd07e9 100644 --- a/Tests/Promise_NonGeneric_Tests.cs +++ b/Tests/Promise_NonGeneric_Tests.cs @@ -431,6 +431,31 @@ public void combined_promise_is_resolved_when_all_promises_are_already_resolved( }); } + [Fact] + public void all_with_rejected_promise() + { + bool resolved = false; + bool rejected = false; + Exception caughtException = null; + Exception exception = new Exception(); + + var promiseA = new Promise(); + var promise = Promise + .All(promiseA, Promise.Rejected(exception)) + .Then(() => resolved = true) + .Catch(ex => + { + caughtException = ex; + rejected = true; + }); + promiseA.ReportProgress(0.5f); + promiseA.Resolve(); + + Assert.Equal(false, resolved); + Assert.Equal(true, rejected); + Assert.Equal(exception, caughtException); + } + [Fact] public void exception_thrown_during_transform_rejects_promise() { From 006c5d5e898fd15b65784042c8a3794de2344402 Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Mon, 23 Jul 2018 17:22:38 +1000 Subject: [PATCH 78/80] Travis CI: switch from XBuild to MSBuild XBuild is deprecated and will be removed in future updates, plus it doesn't support building for .NET Standard 2.0. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ec32d60..637adc7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,5 +6,5 @@ install: - nuget install xunit.runners -Version 1.9.2 -OutputDirectory testrunner script: - - xbuild /p:Configuration=Debug C-Sharp-Promise.sln - - mono ./testrunner/xunit.runners.1.9.2/tools/xunit.console.exe ./bin/Debug/RSG.Promise.Tests.dll \ No newline at end of file + - msbuild /p:Configuration=Debug C-Sharp-Promise.sln + - mono ./testrunner/xunit.runners.1.9.2/tools/xunit.console.exe ./bin/Debug/RSG.Promise.Tests.dll From 69d8613c0ca24a33e019c2517cbde7e1e82fd36c Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Thu, 26 Jul 2018 12:11:28 +1000 Subject: [PATCH 79/80] Rename C-Sharp-Promise RSG.Promise so that Nuget package matches old name --- C-Sharp-Promise.sln | 9 ++++++--- Examples/Example1/Example1.csproj | 4 ++-- Examples/Example2/Example2.csproj | 4 ++-- Examples/Example3/Example3.csproj | 4 ++-- Examples/Example4/Example4.csproj | 4 ++-- Examples/Example5/Example5.csproj | 4 ++-- Tests/Promise.Tests.csproj | 4 ++-- src/{C-Sharp-Promise.csproj => RSG.Promise.csproj} | 0 8 files changed, 18 insertions(+), 15 deletions(-) rename src/{C-Sharp-Promise.csproj => RSG.Promise.csproj} (100%) diff --git a/C-Sharp-Promise.sln b/C-Sharp-Promise.sln index a2e8213..552f606 100644 --- a/C-Sharp-Promise.sln +++ b/C-Sharp-Promise.sln @@ -1,9 +1,9 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 +# Visual Studio 15 +VisualStudioVersion = 15.0.27703.2042 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "C-Sharp-Promise", "src\C-Sharp-Promise.csproj", "{7E1C6A8A-84E7-43C3-AE73-C6E8A9F11AF3}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RSG.Promise", "src\RSG.Promise.csproj", "{7E1C6A8A-84E7-43C3-AE73-C6E8A9F11AF3}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Promise.Tests", "Tests\Promise.Tests.csproj", "{4B4EE1A6-25A3-43D0-BAE9-5E45E31F2816}" EndProject @@ -64,4 +64,7 @@ Global {BDB0135C-B075-47BE-A971-1927BEA6F622} = {79A9B3AC-1942-4441-872C-41CF7BB240A4} {1060401E-1C17-4741-BF1E-9F05B5427775} = {79A9B3AC-1942-4441-872C-41CF7BB240A4} EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {7D2FBA48-64F2-4236-BA93-0AF605D743D8} + EndGlobalSection EndGlobal diff --git a/Examples/Example1/Example1.csproj b/Examples/Example1/Example1.csproj index ba99ed8..9272371 100644 --- a/Examples/Example1/Example1.csproj +++ b/Examples/Example1/Example1.csproj @@ -50,9 +50,9 @@ - + {7e1c6a8a-84e7-43c3-ae73-c6e8a9f11af3} - C-Sharp-Promise + RSG.Promise diff --git a/Examples/Example2/Example2.csproj b/Examples/Example2/Example2.csproj index 3bce523..ad5186d 100644 --- a/Examples/Example2/Example2.csproj +++ b/Examples/Example2/Example2.csproj @@ -50,9 +50,9 @@ - + {7e1c6a8a-84e7-43c3-ae73-c6e8a9f11af3} - C-Sharp-Promise + RSG.Promise diff --git a/Examples/Example3/Example3.csproj b/Examples/Example3/Example3.csproj index d2ad462..4a0929f 100644 --- a/Examples/Example3/Example3.csproj +++ b/Examples/Example3/Example3.csproj @@ -50,9 +50,9 @@ - + {7e1c6a8a-84e7-43c3-ae73-c6e8a9f11af3} - C-Sharp-Promise + RSG.Promise diff --git a/Examples/Example4/Example4.csproj b/Examples/Example4/Example4.csproj index 21d9390..89df58d 100644 --- a/Examples/Example4/Example4.csproj +++ b/Examples/Example4/Example4.csproj @@ -50,9 +50,9 @@ - + {7e1c6a8a-84e7-43c3-ae73-c6e8a9f11af3} - C-Sharp-Promise + RSG.Promise diff --git a/Examples/Example5/Example5.csproj b/Examples/Example5/Example5.csproj index 761f647..e68b478 100644 --- a/Examples/Example5/Example5.csproj +++ b/Examples/Example5/Example5.csproj @@ -50,9 +50,9 @@ - + {7e1c6a8a-84e7-43c3-ae73-c6e8a9f11af3} - C-Sharp-Promise + RSG.Promise diff --git a/Tests/Promise.Tests.csproj b/Tests/Promise.Tests.csproj index 7fb6c87..616ab07 100644 --- a/Tests/Promise.Tests.csproj +++ b/Tests/Promise.Tests.csproj @@ -61,9 +61,9 @@ - + {7e1c6a8a-84e7-43c3-ae73-c6e8a9f11af3} - C-Sharp-Promise + RSG.Promise diff --git a/src/C-Sharp-Promise.csproj b/src/RSG.Promise.csproj similarity index 100% rename from src/C-Sharp-Promise.csproj rename to src/RSG.Promise.csproj From baa92ba276756c0dcfa30684e3a46fa99ee20f2b Mon Sep 17 00:00:00 2001 From: Rory Dungan Date: Thu, 26 Jul 2018 12:12:00 +1000 Subject: [PATCH 80/80] Remove unnecessary nuspec file --- C-Sharp-Promise.nuspec | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 C-Sharp-Promise.nuspec diff --git a/C-Sharp-Promise.nuspec b/C-Sharp-Promise.nuspec deleted file mode 100644 index 96d1df8..0000000 --- a/C-Sharp-Promise.nuspec +++ /dev/null @@ -1,18 +0,0 @@ - - - - $id$ - $version$ - $title$ - $author$ - $author$ - https://github.com/Real-Serious-Games/C-Sharp-Promise - https://github.com/Real-Serious-Games/C-Sharp-Promise - https://avatars0.githubusercontent.com/u/7463833?v=2&s=200 - false - $description$ - First release of nuget package. - Copyright 2014 - C# Promise Promises Futures Async Asynchronous - - \ No newline at end of file