uPromise - Promises Pattern library for Unity (C#), inspired by q.js (for javascript).
Promises are a software abstraction that provides a great way for working with asynchronous code and also provides an abstraction between synchronous and asynchronous functions.
Highlights
- Fluent API / Chaining
- Statically Typed
- Develop sync/async easier and better
- Async and Sync abstraction
- Promises can mitigate the "Pyramid of Doom", meaning having nested lambdas in each other.
- Includes several examples
Supported Platforms:
Windows, Android, iOS, Web (ask to ensure on others if needed)
Unity supported:
Unity, Unity Pro
Requires Unity 3.5.0 or higher.
Download
You can get it from the unity asset store https://www.assetstore.unity3d.com/#/content/15604.Documentation
Promise: a proxy object for a result that is initially unknown.Deferred: the creator of the promise, and will change the result of the promise once the operation has been completed.
What are they used for?
- They replace tradition callbacks e.g. http.Get(onDone, onFail)- Commonly used with data services for HTTP, but can used in any async form.
- Promises can mitigate the "Pyramid of Doom", meaning having nested lambdas in each other.
FAQ:
Q: I'm resolving/rejecting a promise and the .Fail/.Then on the subscribers are not being invoked.A: You are resolving the deferred before returning the promise, make sure that the "Resolving" will be done after (async) returning the promise or see "Creating a Promise (resolved) from non-async" if needed.
Creating a simple Deferred<T>
// Create a new deferred. var deferred = new Deferred<Person>(); // Call an async operation, and pass deferred as parameter. // after async operation has been complete invoke either deferred.Resolve(new Person{ Name = "Joe"}) or deferred.Reject(); // IMPORTANT NOTE: if the operation is not async see: "Creating a Promise from non-async" // THE HttpClient IS NOT INCLUDED WITHIN THIS PACKAGE - ITS JUST AN EXAMPLE. HttpClient.Get<Person>(onDone: response => deferred.Resolve(response.Content), onFail: error => deferred.Reject(error) // Return the Promise. return deferred.Promise;
Resolving a deferred
// Once the async operation finishes, Simply Resolve by calling: deferred.Resolve() OR deferred.Resolve(arg) e.g. deferred.Resolve(new Person{Name = "Joe"}); After resolving the deferred, it will cause the promise to fire the .Then for all its subscribers and the "arg" value will be passed as a parameter to it.
Rejecting a deferred
// Once the async operation finishes, Simply Reject by calling: deferred.Reject() //when no reason need to be returned and just cause the promise to fail. OR deferred.Reject(error) e.g. deferred.Reject(System.Net.HttpStatusCode.BadRequest); After rejecting the deferred, it will cause the promise to fire the .Fail for all its subscribers and the "error" value will be passed as a parameter to it.
Notify a promise
// Once the async operation is going, Simply Notify by calling: deferred.Notify(arg) e.g. deferred.Notify("enquiring data"); After notify the promise, it will cause the promise to fire the .Progress for all its subscribers and the "arg" value will be passed as a parameter to it.
Creating a Promise (resolved) from non-async
TaskFactory.StartNew(x => new Person{ Name = "Joe"}); OR // create a promise with delay (seconds), which will be resolved after the delay parameter. TaskFactory.StartNew(x => new Person{ Name = "Joe"}, delay);
Promise: Then
Invoked after the Deferred has been resolved, with parameter "x" being the resolved arg.
This is similar to .Done, but with some key differences.
- .Then which are subscribed will be executed sequentially, if one of them fails, immediately exit (without invoking any others) and invoke .Fail.
- .Then is capable of changing the promise into a new promise
// Assuming that personService has a method .Get(id) which returns a Promise<Person> personService.Get(id: 123) .Then(x => { Debug.Log(string.Format("Person.Name={0}", x.Name)); }); // Changing Promise by Then Example personService.Get(id: 123) .Then(x => { Debug.Log(string.Format("Person.Name={0}", x.Name)); return x.Name; //this will change the promise from Promise<Person> to Promise<string>. });
Promise: Done
Invoked after the Deferred has been fully resolved, with parameter "x" being the resolved arg.
By fully resolved means, all .Then subscribed has been finished successful.
// Assuming that personService has a method .Get(id) which returns a Promise<Person> personService.Get(id: 123) .Done(x => { Debug.Log(string.Format("Person.Name={0}", x.Name)); });
Promise: Fail
Fail will be invoked in different ways:
- Deferred has been rejected e.g. deferred.Reject()
- An error has occurred during .Then()
This is very similar to try/catch, were Fail being the catch.
// Assuming that personService has a method .Get(id) which returns a Promise<Person> // This will catch all Fail's of any type. personService.Get(id: 123) .Fail(x => { Debug.Log(string.Format("An error has occurred. x={0}", x)); }); // This will catch ONLY Fail's which are of specific type: System.Net.HttpStatusCode personService.Get(id: 123) .Fail<System.Net.HttpStatusCode>(x => { Debug.Log(string.Format("An error has occurred. x={0}", x)); });
Promise: Finally
This is very similar to try/catch/finally, were Finally being the finally, meaning it will be invoked either when a promise is fully resolved (e.g. not when .Then is complete but all the chains are complete) or when it fails.
// Assuming that personService has a method .Get(id) which returns a Promise<Person> personService.Get(id: 123) .Finally(() => { Debug.Log("Promise ends."); });
Promise: Progress
Invoked by calling Notify on the Deferred, with parameter "x" being the .Notify arg.
// Assuming that personService has a method .Get(id) which returns a Promise<Person> personService.Get(id: 123) .Progress<string>(x => { Debug.Log(string.Format("The progress status={0}", x)) });
Promise Method Chaining
Promise methods can be chained together
// Assuming that personService has a method .Get(id) which returns a Promise<Person> personService.Get(id: 123) .Then(x => //x is Person { Debug.Log(string.Format("Person.Name={0}", x.Name)); return x.Name; //this will change the promise from Promise<Person> to Promise<string> }) .Then(x => //x is string because of the above. { Debug.Log(string.Format("x={0}", x)); }) .Progress<string>(x => Debug.Log(string.Format("The progress status={0}", x))) .Fail<System.Net.HttpStatusCode>(x => Debug.Log(string.Format("An error has occurred. x={0}", x))) .Fail(x => Debug.Log(string.Format("An error has occurred. x={0}", x))) .Finally(() => Debug.Log("Promise ends."));
Promise.All
Wait for all promises specified to be resolved (parallel) in order for the All Promise to be resolved, if any of them fails, it will immediately reject the promise.
// Assuming that personService has a method .Get(id) which returns a Promise<Person> var personPromise = personService.Get(id: 123); var personPromise2 = personService.Get(id: 2); Promise.All(personPromise, personPromise2) .Then(x => { Debug.Log(string.Format("All Sample [Then] all complete. result. x[0]={0} x[1]={1}", x)); return (Person) x[0]; // return first value which is the first person. })
Change Log
---------------------------------------
v1.4.0 - 14/05/2014
---------------------------------------
Change:
- Implemented Promise.Reject and Promise.Reject<T> which allows you to simply return a rejected promise.
usage: return Promise.Reject(PlayerQuestErrorState.QuestLogFull);
- Implemented Then parameterless.
- Implemented Promise All Sequentially, like Promise.All but they will run one after the other.
usage: Promise.AllSequentially(Current.Shutdown, sceneControllerTo.Initialize).
Break Changes:
- Done has been changed: It will now return void which can be used at the end of the chain.
- TaskFactory has been renamed to PromiseFactory.
Fix:
- Fixed an issue on TaskFactory.StartNewDelayed which caused it to never resolve.
- TaskFactory cancellation is now passed properly to functions from signatures.
v1.4.0 - 14/05/2014
---------------------------------------
Change:
- Implemented Promise.Reject and Promise.Reject<T> which allows you to simply return a rejected promise.
usage: return Promise.Reject(PlayerQuestErrorState.QuestLogFull);
- Implemented Then parameterless.
- Implemented Promise All Sequentially, like Promise.All but they will run one after the other.
usage: Promise.AllSequentially(Current.Shutdown, sceneControllerTo.Initialize).
Break Changes:
- Done has been changed: It will now return void which can be used at the end of the chain.
- TaskFactory has been renamed to PromiseFactory.
Fix:
- Fixed an issue on TaskFactory.StartNewDelayed which caused it to never resolve.
- TaskFactory cancellation is now passed properly to functions from signatures.
---------------------------------------
v1.3.2 - 23/04/2014
---------------------------------------
- Then Promise chaining (non generic support).
- Fixes in Finally when using Promise<T>, and an error occurs it was causing it to crash.
---------------------------------------
v1.3.1 - 19/03/2014
---------------------------------------
- Cleaned several Debug.Logs
v1.3.2 - 23/04/2014
---------------------------------------
- Then Promise chaining (non generic support).
- Fixes in Finally when using Promise<T>, and an error occurs it was causing it to crash.
---------------------------------------
v1.3.1 - 19/03/2014
---------------------------------------
- Cleaned several Debug.Logs
---------------------------------------
v1.2 - 06/03/2014
---------------------------------------
- Implemented Promise<T>.Fail(Action<object>) which was missing to match Promise (non generic)
- Implemented Promise<T>.Finally(Action) which was missing to match Promise (non generic)
- TaskFactory:
- Created StartNewDeferred which will create a new deferred without being automatically resolved.
- Recreated methods to ensure both StartNew for Promise<T> and Promise will have exact same functionality and signatures.
- Refactoring
---------------------------------------
v1.1 - 27/02/2014
---------------------------------------
- Reworked .Fail method to have a better API.
- Implemented .Fail<TFail> method which listens only to the specified type.
- .Fail<object> and .Fail are now triggered always when failed.
- Implemented Reject<TFail>(TFail arg) which allows fail for specified type.
---------------------------------------
v1.0 - 24/02/2014
---------------------------------------
initial release
No comments:
Post a Comment