diff --git a/C-Sharp-Promise.csproj b/C-Sharp-Promise.csproj
index 8b99fa3..8755b48 100644
--- a/C-Sharp-Promise.csproj
+++ b/C-Sharp-Promise.csproj
@@ -41,6 +41,7 @@
+
diff --git a/Promise.cs b/Promise.cs
index d96b9ff..8f5f3e0 100644
--- a/Promise.cs
+++ b/Promise.cs
@@ -10,12 +10,12 @@ namespace RSG
/// Implements a C# promise.
/// https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise
///
- public interface IPromise
+ public interface IPromise: IPromiseBase
{
///
/// Set the name of the promise, useful for debugging.
///
- IPromise WithName(string name);
+ new IPromise WithName(string name);
///
/// Completes the promise.
@@ -31,15 +31,10 @@ public interface IPromise
///
void Done(Action onResolved);
- ///
- /// Complete the promise. Adds a default error handler.
- ///
- void Done();
-
///
/// Handle errors for the promise.
///
- IPromise Catch(Action onRejected);
+ new IPromise Catch(Action onRejected);
///
/// Add a resolved callback that chains a value promise (optionally converting to a different value type).
@@ -116,17 +111,18 @@ public interface IPromise
/// Yields the value from the first promise that has resolved.
///
IPromise ThenRace(Func> chain);
- }
- ///
- /// Interface for a promise that can be rejected.
- ///
- public interface IRejectable
- {
///
- /// Reject the promise with an exception.
+ /// Add a finally callback.
+ /// Finally callbacks will always be called, even if any preceding promise is rejected, or encounters an error.
+ ///
+ new 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.
///
- void Reject(Exception ex);
+ IPromise Finally(Func> onComplete);
}
///
@@ -140,79 +136,28 @@ public interface IPendingPromise : IRejectable
void Resolve(PromisedT value);
}
- ///
- /// Specifies the state of a promise.
- ///
- public enum PromiseState
- {
- Pending, // The promise is in-flight.
- Rejected, // The promise has been rejected.
- Resolved // The promise has been resolved.
- };
-
///
/// Implements a C# promise.
/// https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise
///
- public class Promise : IPromise, IPendingPromise, IPromiseInfo
+ public class Promise : Promise_Base, IPromise, IPendingPromise
{
- ///
- /// The exception when the promise is rejected.
- ///
- private Exception rejectionException;
-
///
/// The value when the promises is resolved.
///
private PromisedT resolveValue;
- ///
- /// Error handler.
- ///
- private List rejectHandlers;
-
///
/// Completed handlers that accept a value.
///
private List> resolveCallbacks;
private List resolveRejectables;
- ///
- /// ID of the promise, useful for debugging.
- ///
- public int Id { get; private set; }
-
- ///
- /// Name of the promise, when set, useful for debugging.
- ///
- public string Name { get; private set; }
-
- ///
- /// Tracks the current state of the promise.
- ///
- public PromiseState CurState { get; private set; }
-
- public Promise()
- {
- this.CurState = PromiseState.Pending;
- this.Id = ++Promise.nextPromiseId;
+ public Promise() : base()
+ { }
- if (Promise.EnablePromiseTracking)
- {
- Promise.pendingPromises.Add(this);
- }
- }
-
- public Promise(Action, Action> resolver)
+ public Promise(Action, Action> resolver) : this()
{
- this.CurState = PromiseState.Pending;
- this.Id = ++Promise.nextPromiseId;
-
- if (Promise.EnablePromiseTracking)
- {
- Promise.pendingPromises.Add(this);
- }
-
try
{
resolver(
@@ -229,19 +174,6 @@ public Promise(Action, Action> resolver)
}
}
- ///
- /// Add a rejection handler for this promise.
- ///
- private void AddRejectHandler(Action onRejected, IRejectable rejectable)
- {
- if (rejectHandlers == null)
- {
- rejectHandlers = new List();
- }
-
- rejectHandlers.Add(new RejectHandler() { callback = onRejected, rejectable = rejectable }); ;
- }
-
///
/// Add a resolve handler for this promise.
///
@@ -261,49 +193,16 @@ private void AddResolveHandler(Action onResolved, IRejectable rejecta
resolveRejectables.Add(rejectable);
}
- ///
- /// Invoke a single handler.
- ///
- private void InvokeHandler(Action callback, IRejectable rejectable, T value)
- {
-// Argument.NotNull(() => callback);
-// Argument.NotNull(() => rejectable);
-
- try
- {
- callback(value);
- }
- catch (Exception ex)
- {
- rejectable.Reject(ex);
- }
- }
-
///
/// Helper function clear out all handlers after resolution or rejection.
///
- private void ClearHandlers()
+ protected override void ClearHandlers()
{
- rejectHandlers = null;
+ base.ClearHandlers();
resolveCallbacks = null;
resolveRejectables = null;
}
- ///
- /// Invoke all reject handlers.
- ///
- private void InvokeRejectHandlers(Exception ex)
- {
-// Argument.NotNull(() => ex);
-
- if (rejectHandlers != null)
- {
- rejectHandlers.Each(handler => InvokeHandler(handler.callback, handler.rejectable, ex));
- }
-
- ClearHandlers();
- }
-
///
/// Invoke all resolve handlers.
///
@@ -319,29 +218,6 @@ private void InvokeResolveHandlers(PromisedT value)
ClearHandlers();
}
- ///
- /// Reject the promise with an exception.
- ///
- public void Reject(Exception ex)
- {
-// Argument.NotNull(() => 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);
- }
-
- rejectionException = ex;
- CurState = PromiseState.Rejected;
-
- if (Promise.EnablePromiseTracking)
- {
- Promise.pendingPromises.Remove(this);
- }
-
- InvokeRejectHandlers(ex);
- }
-
///
/// Resolve the promise with a particular value.
///
@@ -390,7 +266,33 @@ public void Done(Action onResolved)
}
///
- /// Complete the promise. Adds a default error handler.
+ /// Completes the promise.
+ /// onResolved is called on successful completion.
+ /// onRejected is called on error.
+ ///
+ void IPromiseBase.Done(Action onResolved, Action onRejected)
+ {
+ Then((x) => { onResolved(new PromiseResult(x)); }, onRejected)
+ .Catch(ex =>
+ Promise.PropagateUnhandledException(this, ex)
+ );
+ }
+
+ ///
+ /// Completes the promise.
+ /// onResolved is called on successful completion.
+ /// Adds a default error handler.
+ ///
+ void IPromiseBase.Done(Action onResolved)
+ {
+ Then((x) => { onResolved(new PromiseResult(x)); })
+ .Catch(ex =>
+ Promise.PropagateUnhandledException(this, ex)
+ );
+ }
+
+ ///
+ /// Complete the promise. Adds a defualt error handler.
///
public void Done()
{
@@ -408,6 +310,11 @@ public IPromise WithName(string name)
return this;
}
+ IPromiseBase IPromiseBase.WithName(string name)
+ {
+ return WithName(name);
+ }
+
///
/// Handle errors for the promise.
///
@@ -435,6 +342,11 @@ public IPromise Catch(Action onRejected)
return resultPromise;
}
+ IPromiseBase IPromiseBase.Catch(Action onRejected)
+ {
+ return Catch(onRejected);
+ }
+
///
/// Add a resolved callback that chains a value promise (optionally converting to a different value type).
///
@@ -451,6 +363,11 @@ public IPromise Then(Func onResolved)
return Then(onResolved, null);
}
+ IPromiseBase IPromiseBase.Then(Func onResolved)
+ {
+ return Then((x) => { onResolved(new PromiseResult(x)); }, null);
+ }
+
///
/// Add a resolved callback.
///
@@ -459,6 +376,11 @@ public IPromise Then(Action onResolved)
return Then(onResolved, null);
}
+ IPromiseBase IPromiseBase.Then(Action onResolved)
+ {
+ return Then((x) => { onResolved(new PromiseResult(x)); }, null);
+ }
+
///
/// Add a resolved callback and a rejected callback.
/// The resolved callback chains a value promise (optionally converting to a different value type).
@@ -537,6 +459,11 @@ public IPromise Then(Func onResolved, Action onR
return resultPromise;
}
+ IPromiseBase IPromiseBase.Then(Func onResolved, Action onRejected)
+ {
+ return Then((x) => { onResolved(new PromiseResult(x)); }, onRejected);
+ }
+
///
/// Add a resolved callback and a rejected callback.
///
@@ -570,6 +497,11 @@ public IPromise Then(Action onResolved, Action
return resultPromise;
}
+ IPromiseBase IPromiseBase.Then(Action onResolved, Action onRejected)
+ {
+ return Then((x) => { onResolved(new PromiseResult(x)); }, onRejected);
+ }
+
///
/// Return a new promise with a different value.
/// May also change the type of the value.
@@ -633,6 +565,11 @@ public IPromise ThenAll(Func> chain)
return Then(value => Promise.All(chain(value)));
}
+ IPromiseBase IPromiseBase.ThenAll(Func> chain)
+ {
+ return ThenAll((x) => { return chain(); });
+ }
+
///
/// Returns a promise that resolves when all of the promises in the enumerable argument have resolved.
/// Returns a promise of a collection of the resolved results.
@@ -777,5 +714,32 @@ 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);
+ }
+
+ IPromiseBase IPromiseBase.Finally(Action onComplete)
+ {
+ return Finally(onComplete);
+ }
}
}
\ No newline at end of file
diff --git a/Promise_Base.cs b/Promise_Base.cs
new file mode 100644
index 0000000..1f82478
--- /dev/null
+++ b/Promise_Base.cs
@@ -0,0 +1,280 @@
+using System;
+using System.Collections.Generic;
+using RSG.Promises;
+
+namespace RSG
+{
+ public interface IPromiseBase
+ {
+ ///
+ /// Set the name of the promise, useful for debugging.
+ ///
+ IPromiseBase WithName(string name);
+
+ ///
+ /// Completes the promise.
+ /// onResolved is called on successful completion.
+ /// onRejected is called on error.
+ ///
+ void Done(Action onResolved, Action onRejected);
+
+ ///
+ /// Completes the promise.
+ /// onResolved is called on successful completion.
+ /// Adds a default error handler.
+ ///
+ void Done(Action onResolved);
+
+ ///
+ /// Complete the promise. Adds a default error handler.
+ ///
+ void Done();
+
+ ///
+ /// Handle errors for the promise.
+ ///
+ IPromiseBase Catch(Action onRejected);
+
+ ///
+ /// Add a resolved callback that chains a non-value promise.
+ ///
+ IPromiseBase Then(Func onResolved);
+
+ ///
+ /// Add a resolved callback.
+ ///
+ IPromiseBase Then(Action onResolved);
+
+ ///
+ /// Add a resolved callback and a rejected callback.
+ /// The resolved callback chains a non-value promise.
+ ///
+ IPromiseBase Then(Func onResolved, Action onRejected);
+
+ ///
+ /// Add a resolved callback and a rejected callback.
+ ///
+ IPromiseBase Then(Action onResolved, Action onRejected);
+
+ ///
+ /// Chain an enumerable of promises, all of which must resolve.
+ /// The resulting promise is resolved when all of the promises have resolved.
+ /// It is rejected as soon as any of the promises have been rejected.
+ ///
+ IPromiseBase ThenAll(Func> chain);
+
+ ///
+ /// Add a finally callback.
+ /// Finally callbacks will always be called, even if any preceding promise is rejected, or encounters an error.
+ ///
+ IPromiseBase Finally(Action onComplete);
+ }
+
+ ///
+ /// Interface for a promise that can be rejected.
+ ///
+ public interface IRejectable
+ {
+ ///
+ /// Reject the promise with an exception.
+ ///
+ void Reject(Exception ex);
+ }
+
+ ///
+ /// Arguments to the UnhandledError event.
+ ///
+ public class ExceptionEventArgs : EventArgs
+ {
+ internal ExceptionEventArgs(Exception exception)
+ {
+ // Argument.NotNull(() => exception);
+
+ this.Exception = exception;
+ }
+
+ public Exception Exception
+ {
+ get;
+ private set;
+ }
+ }
+
+ ///
+ /// Represents a handler invoked when the promise is rejected.
+ ///
+ public struct RejectHandler
+ {
+ ///
+ /// Callback fn.
+ ///
+ public Action callback;
+
+ ///
+ /// The promise that is rejected when there is an error while invoking the handler.
+ ///
+ public IRejectable rejectable;
+ }
+
+ public class PromiseResult
+ {
+ public static readonly PromiseResult None = new PromiseResult();
+
+ public readonly bool hasValue;
+ public readonly object Result;
+
+ public PromiseResult()
+ {
+ hasValue = false;
+ }
+
+ public PromiseResult(object value)
+ {
+ hasValue = true;
+ Result = value;
+ }
+ }
+
+ ///
+ /// Specifies the state of a promise.
+ ///
+ public enum PromiseState
+ {
+ Pending, // The promise is in-flight.
+ Rejected, // The promise has been rejected.
+ Resolved // The promise has been resolved.
+ };
+
+ ///
+ /// Used to list information of pending promises.
+ ///
+ public interface IPromiseInfo
+ {
+ ///
+ /// Id of the promise.
+ ///
+ int Id { get; }
+
+ ///
+ /// Human-readable name for the promise.
+ ///
+ string Name { get; }
+ }
+
+ public abstract class Promise_Base : IPromiseInfo
+ {
+ ///
+ /// The exception when the promise is rejected.
+ ///
+ protected Exception rejectionException;
+
+ ///
+ /// Error handlers.
+ ///
+ protected List rejectHandlers;
+
+ ///
+ /// ID of the promise, useful for debugging.
+ ///
+ public int Id { get; protected set; }
+
+ ///
+ /// Name of the promise, when set, useful for debugging.
+ ///
+ public string Name { get; protected set; }
+
+ ///
+ /// Tracks the current state of the promise.
+ ///
+ public PromiseState CurState { get; protected set; }
+
+ public Promise_Base()
+ {
+ this.CurState = PromiseState.Pending;
+ this.Id = ++Promise.nextPromiseId;
+
+ if (Promise.EnablePromiseTracking)
+ {
+ Promise.pendingPromises.Add(this);
+ }
+ }
+
+ ///
+ /// Add a rejection handler for this promise.
+ ///
+ protected void AddRejectHandler(Action onRejected, IRejectable rejectable)
+ {
+ if (rejectHandlers == null)
+ {
+ rejectHandlers = new List();
+ }
+
+ rejectHandlers.Add(new RejectHandler()
+ {
+ callback = onRejected,
+ rejectable = rejectable
+ });
+ }
+
+ ///
+ /// Invoke a single handler.
+ ///
+ protected void InvokeHandler(Action callback, IRejectable rejectable, T value)
+ {
+ // Argument.NotNull(() => callback);
+ // Argument.NotNull(() => rejectable);
+
+ try
+ {
+ callback(value);
+ }
+ catch (Exception ex)
+ {
+ rejectable.Reject(ex);
+ }
+ }
+
+ protected virtual void ClearHandlers()
+ {
+ rejectHandlers = null;
+ }
+
+ ///
+ /// Invoke all reject handlers.
+ ///
+ protected void InvokeRejectHandlers(Exception ex)
+ {
+ // Argument.NotNull(() => ex);
+
+ if (rejectHandlers != null)
+ {
+ rejectHandlers.Each(handler => InvokeHandler(handler.callback, handler.rejectable, ex));
+ }
+
+ ClearHandlers();
+ }
+
+ ///
+ /// Reject the promise with an exception.
+ ///
+ public void Reject(Exception ex)
+ {
+ // Argument.NotNull(() => 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);
+ }
+
+ rejectionException = ex;
+ CurState = PromiseState.Rejected;
+
+ if (Promise.EnablePromiseTracking)
+ {
+ Promise.pendingPromises.Remove(this);
+ }
+
+ InvokeRejectHandlers(ex);
+ }
+ }
+}
diff --git a/Promise_NonGeneric.cs b/Promise_NonGeneric.cs
index cc9905a..f0264d1 100644
--- a/Promise_NonGeneric.cs
+++ b/Promise_NonGeneric.cs
@@ -10,12 +10,12 @@ namespace RSG
/// 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
///
- public interface IPromise
+ public interface IPromise : IPromiseBase
{
///
/// Set the name of the promise, useful for debugging.
///
- IPromise WithName(string name);
+ new IPromise WithName(string name);
///
/// Completes the promise.
@@ -31,15 +31,10 @@ public interface IPromise
///
void Done(Action onResolved);
- ///
- /// Complete the promise. Adds a default error handler.
- ///
- void Done();
-
///
/// Handle errors for the promise.
///
- IPromise Catch(Action onRejected);
+ new IPromise Catch(Action onRejected);
///
/// Add a resolved callback that chains a value promise (optionally converting to a different value type).
@@ -78,7 +73,7 @@ public interface IPromise
/// The resulting promise is resolved when all of the promises have resolved.
/// It is rejected as soon as any of the promises have been rejected.
///
- IPromise ThenAll(Func> chain);
+ new IPromise ThenAll(Func> chain);
///
/// Chain an enumerable of promises, all of which must resolve.
@@ -108,75 +103,36 @@ public interface IPromise
/// Returns a promise that resolves when the first of the promises has resolved.
///
IPromise ThenRace(Func>> chain);
- }
-
- ///
- /// Interface for a promise that can be rejected or resolved.
- ///
- public interface IPendingPromise : IRejectable
- {
- ///
- /// Resolve the promise with a particular value.
- ///
- void Resolve();
- }
- ///
- /// Used to list information of pending promises.
- ///
- public interface IPromiseInfo
- {
///
- /// Id of the promise.
+ /// Add a finally callback.
+ /// Finally callbacks will always be called, even if any preceding promise is rejected, or encounters an error.
///
- int Id { get; }
+ new IPromise Finally(Action onComplete);
///
- /// Human-readable name for the promise.
+ /// Add a finally callback.
+ /// Finally callbacks will always be called, even if any preceding promise is rejected, or encounters an error.
///
- string Name { get; }
+ IPromise Finally(Func> onComplete);
}
///
- /// Arguments to the UnhandledError event.
- ///
- public class ExceptionEventArgs : EventArgs
- {
- internal ExceptionEventArgs(Exception exception)
- {
-// Argument.NotNull(() => exception);
-
- this.Exception = exception;
- }
-
- public Exception Exception
- {
- get;
- private set;
- }
- }
-
- ///
- /// Represents a handler invoked when the promise is rejected.
+ /// Interface for a promise that can be rejected or resolved.
///
- public struct RejectHandler
+ public interface IPendingPromise : IRejectable
{
///
- /// Callback fn.
- ///
- public Action callback;
-
- ///
- /// The promise that is rejected when there is an error while invoking the handler.
+ /// Resolve the promise with a particular value.
///
- public IRejectable rejectable;
+ void Resolve();
}
///
/// 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
///
- public class Promise : IPromise, IPendingPromise, IPromiseInfo
+ public class Promise : Promise_Base, IPromise, IPendingPromise
{
///
/// Set to true to enable tracking of promises.
@@ -213,16 +169,6 @@ public static IEnumerable GetPendingPromises()
return pendingPromises;
}
- ///
- /// The exception when the promise is rejected.
- ///
- private Exception rejectionException;
-
- ///
- /// Error handlers.
- ///
- private List rejectHandlers;
-
///
/// Represents a handler invoked when the promise is resolved.
///
@@ -244,38 +190,11 @@ public struct ResolveHandler
///
private List resolveHandlers;
- ///
- /// ID of the promise, useful for debugging.
- ///
- public int Id { get; private set; }
-
- ///
- /// Name of the promise, when set, useful for debugging.
- ///
- public string Name { get; private set; }
-
- ///
- /// Tracks the current state of the promise.
- ///
- public PromiseState CurState { get; private set; }
-
- public Promise()
- {
- this.CurState = PromiseState.Pending;
- if (EnablePromiseTracking)
- {
- pendingPromises.Add(this);
- }
- }
+ public Promise() : base()
+ { }
- public Promise(Action> resolver)
+ public Promise(Action> resolver): this()
{
- this.CurState = PromiseState.Pending;
- if (EnablePromiseTracking)
- {
- pendingPromises.Add(this);
- }
-
try
{
resolver(
@@ -292,23 +211,6 @@ public Promise(Action> resolver)
}
}
- ///
- /// Add a rejection handler for this promise.
- ///
- private void AddRejectHandler(Action onRejected, IRejectable rejectable)
- {
- if (rejectHandlers == null)
- {
- rejectHandlers = new List();
- }
-
- rejectHandlers.Add(new RejectHandler()
- {
- callback = onRejected,
- rejectable = rejectable
- });
- }
-
///
/// Add a resolve handler for this promise.
///
@@ -365,27 +267,12 @@ private void InvokeResolveHandler(Action callback, IRejectable rejectable)
///
/// Helper function clear out all handlers after resolution or rejection.
///
- private void ClearHandlers()
+ override protected void ClearHandlers()
{
- rejectHandlers = null;
+ base.ClearHandlers();
resolveHandlers = null;
}
- ///
- /// Invoke all reject handlers.
- ///
- private void InvokeRejectHandlers(Exception ex)
- {
-// Argument.NotNull(() => ex);
-
- if (rejectHandlers != null)
- {
- rejectHandlers.Each(handler => InvokeRejectHandler(handler.callback, handler.rejectable, ex));
- }
-
- ClearHandlers();
- }
-
///
/// Invoke all resolve handlers.
///
@@ -399,30 +286,6 @@ private void InvokeResolveHandlers()
ClearHandlers();
}
- ///
- /// Reject the promise with an exception.
- ///
- public void Reject(Exception ex)
- {
-// Argument.NotNull(() => 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);
- }
-
- rejectionException = ex;
- CurState = PromiseState.Rejected;
-
- if (EnablePromiseTracking)
- {
- pendingPromises.Remove(this);
- }
-
- InvokeRejectHandlers(ex);
- }
-
-
///
/// Resolve the promise with a particular value.
///
@@ -469,6 +332,32 @@ public void Done(Action onResolved)
);
}
+ ///
+ /// Completes the promise.
+ /// onResolved is called on successful completion.
+ /// onRejected is called on error.
+ ///
+ void IPromiseBase.Done(Action onResolved, Action onRejected)
+ {
+ Then(() => { onResolved(PromiseResult.None); }, onRejected)
+ .Catch(ex =>
+ Promise.PropagateUnhandledException(this, ex)
+ );
+ }
+
+ ///
+ /// Completes the promise.
+ /// onResolved is called on successful completion.
+ /// Adds a default error handler.
+ ///
+ void IPromiseBase.Done(Action onResolved)
+ {
+ Then(() => { onResolved(PromiseResult.None); })
+ .Catch(ex =>
+ Promise.PropagateUnhandledException(this, ex)
+ );
+ }
+
///
/// Complete the promise. Adds a defualt error handler.
///
@@ -488,6 +377,11 @@ public IPromise WithName(string name)
return this;
}
+ IPromiseBase IPromiseBase.WithName(string name)
+ {
+ return WithName(name);
+ }
+
///
/// Handle errors for the promise.
///
@@ -515,6 +409,11 @@ public IPromise Catch(Action onRejected)
return resultPromise;
}
+ IPromiseBase IPromiseBase.Catch(Action onRejected)
+ {
+ return Catch(onRejected);
+ }
+
///
/// Add a resolved callback that chains a value promise (optionally converting to a different value type).
///
@@ -531,6 +430,11 @@ public IPromise Then(Func onResolved)
return Then(onResolved, null);
}
+ IPromiseBase IPromiseBase.Then(Func onResolved)
+ {
+ return Then(() => { onResolved(PromiseResult.None); });
+ }
+
///
/// Add a resolved callback.
///
@@ -539,6 +443,11 @@ public IPromise Then(Action onResolved)
return Then(onResolved, null);
}
+ IPromiseBase IPromiseBase.Then(Action onResolved)
+ {
+ return Then(() => { onResolved(PromiseResult.None); });
+ }
+
///
/// Add a resolved callback and a rejected callback.
/// The resolved callback chains a value promise (optionally converting to a different value type).
@@ -617,6 +526,11 @@ public IPromise Then(Func onResolved, Action onRejected)
return resultPromise;
}
+ IPromiseBase IPromiseBase.Then(Func onResolved, Action onRejected)
+ {
+ return Then(() => { onResolved(PromiseResult.None); }, onRejected);
+ }
+
///
/// Add a resolved callback and a rejected callback.
///
@@ -650,6 +564,11 @@ public IPromise Then(Action onResolved, Action onRejected)
return resultPromise;
}
+ IPromiseBase IPromiseBase.Then(Action onResolved, Action onRejected)
+ {
+ return Then(() => { onResolved(PromiseResult.None); }, onRejected);
+ }
+
///
/// Helper function to invoke or register resolve/reject handlers.
///
@@ -680,6 +599,11 @@ public IPromise ThenAll(Func> chain)
return Then(() => Promise.All(chain()));
}
+ IPromiseBase IPromiseBase.ThenAll(Func> chain)
+ {
+ return ThenAll(chain);
+ }
+
///
/// Chain an enumerable of promises, all of which must resolve.
/// Converts to a non-value promise.
@@ -876,5 +800,32 @@ internal static void PropagateUnhandledException(object sender, Exception ex)
unhandlerException(sender, new ExceptionEventArgs(ex));
}
}
+
+ 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(); });
+ }
+
+ IPromiseBase IPromiseBase.Finally(Action onComplete)
+ {
+ return Finally(onComplete);
+ }
}
}
\ No newline at end of file
diff --git a/Tests/PromiseTests.cs b/Tests/PromiseTests.cs
index 2847516..c867f71 100644
--- a/Tests/PromiseTests.cs
+++ b/Tests/PromiseTests.cs
@@ -986,5 +986,100 @@ public void exception_during_Then_onResolved_triggers_error_hander()
Assert.Equal(0, callback);
Assert.Equal(1, errorCallback);
}
+
+ [Fact]
+ public void finally_is_called_after_resolve()
+ {
+ var promise = new Promise();
+ var callback = 0;
+
+ promise.Finally(() =>
+ {
+ ++callback;
+ }).Then((x) => { });
+
+ 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 600fa55..46309bb 100644
--- a/Tests/Promise_NonGeneric_Tests.cs
+++ b/Tests/Promise_NonGeneric_Tests.cs
@@ -1110,5 +1110,100 @@ public void inner_exception_handled_by_outer_promise_with_results()
Promise.UnhandledException -= handler;
}
}
+
+ [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);
+ }
}
}