Coder Social home page Coder Social logo

real-serious-games / c-sharp-promise Goto Github PK

View Code? Open in Web Editor NEW
1.2K 1.2K 149.0 2.32 MB

Promises library for C# for management of asynchronous operations.

License: MIT License

C# 100.00%
async-operation c-sharp game-development promise unity unity3d

c-sharp-promise's Introduction

C-Sharp-Promise Build Status NuGet

Promises/A+ logo

Promises library for C# for management of asynchronous operations.

Inspired by JavaScript promises, but slightly different.

Used by Real Serious Games for building serious games and simulations on Unity3d.

If you are interested in using promises for game development and Unity please see this article.

Recent Updates

  • v3.0 (15 Feburary 2018)
    • Finally has been modified to work in a way consistent to Promise.prototype.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
    • 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

Contents

Table of Contents generated with DocToc

Understanding Promises

To learn about promises:

Promises/A+ Spec

This promise library conforms to the Promises/A+ Spec (at least, as far as is possible with C#):

Getting the DLL

The DLL can be installed via nuget. Use the Package Manager UI or console in Visual Studio or use nuget from the command line.

See here for instructions on installing a package via nuget: http://docs.nuget.org/docs/start-here/using-the-package-manager-console

The package to search for is RSG.Promise.

Getting the Code

You can get the code by cloning the github repository. You can do this in a UI like SourceTree or you can do it from the command line as follows:

git clone https://github.com/Real-Serious-Games/C-Sharp-Promise.git

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;

Create a promise before you start the async operation:

var promise = new Promise<string>();

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);

The promise is rejected on error/exception:

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<string> Download(string url)
{
    var promise = new Promise<string>();    // Create promise.
    using (var client = new WebClient())
    {
        client.DownloadStringCompleted +=   // Monitor event for download completed.
            (s, ev) =>
            {
                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.
    }

    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<string>((resolve, reject) =>
{
    using (var client = new WebClient())
    {
        client.DownloadStringCompleted +=   // Monitor event for download completed.
            (s, ev) =>
            {
                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.
    }
});

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)
    );

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!")
    );

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!")
    );

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);
        }
    );

As is demonstrated the type of the value can also be changed during transformation. In the previous snippet a Promise<string> is transformed to a Promise<string[]>.

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.

Unhandled Errors

When Catch is omitted exceptions go silently unhandled. This is an acknowledged issue with the Promises pattern.

We handle this in a similar way to the JavaScript 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.

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;

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!");
}

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:

var resolvedPromise = Promise<string>.Resolved("some result");

var rejectedPromise = Promise<string>.Rejected(someException);

Interfaces

The class Promise implements the following interfaces:

  • IPromise<T> Interface to await promise resolution.
  • IPendingPromise<T> 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.

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<string>();
urls.Add("www.google.com");
urls.Add("www.yahoo.com");

Promise<string[]>
    .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.
        )
    )
    .Done(links =>              // Receives the flattened collection of links from all pages at once.
    {
        foreach (var link in links)
        {
            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<string>[]                  // 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<string>[]                  // 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(() => ...);  // No result is passed because the previous operation returned nothing.

Promises that have no Results (a non-value promise)

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<T> and implements the similar interfaces: IPromise and IPendingPromise.

Promise<T> 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"));

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<T> 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);
        });
}

Usage:

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")
);

The inputs can also be passed as a collection:

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<IPromise>)(() => 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<IPromise>).

The cast can easily be removed by converting the inner anonymous function to an actual function which I'll call PrepAnimation:

private Func<IPromise> PrepAnimation(string animName)
{
    return () => RunAnimation(animName);
}

var animations = animationNames.Select(animName => PrepAnimation(animName));
var sequence = Promise.Sequence(animations);
sequence
    .Done(() =>
    {
        // All animations have completed in sequence.
    });

Holy cow... we've just careened into 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")
    )
);

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

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 Example
{
    private IPromiseTimer promiseTimer;

    Example()
    {
        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);

        // 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!"));
}

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)
{
    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;
    });
}

PromiseTimer.WaitWhile

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

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

  • Example1
    • Example of downloading text from a URL using a promise.
  • Example2
    • 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.
    • Includes both error handling and a completion handler.
  • Example4
    • 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.

c-sharp-promise's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

c-sharp-promise's Issues

How can I wait for a promise result in a HttpActionResult method?

Hi there!

What the title says, basically.

I have an HttpActionResult in my API that calls for a method in a Third-party API.
I have the following code:

var aux = thirdPartyApiClient.Model.CreateModel(myModel);
            Model myResult = new Model();
            aux.Catch((exception) => {
                throw new Exception(exception.Message);
            });
            aux.Then((model) =>
            {
                myResult = model;
                //return this.Ok(model.Code);
            });

The problem is - I don't know in the moment how to wait for this result before returning to my application that updated result.

How can I properly wait for this result, for all the method to finish (CreateModel method) before returning something as HttpActionResult?

How to keep the "same" promise when chained?

Hey guys :)

I just stumbled upon a small issue.
I'm using the code below:

public static IPromise<ProcessResult> ChmodAndExecute(string filePath)
{
	Promise<ProcessResult> promise = new Promise<ProcessResult>();

	ExecuteProcess("chmod", "+x \"" + filePath + "\"")
	.Then(processResult => 
	{
		return ExecuteProcess(filePath);
	})
	.Catch(exception =>
	{
		promise.Reject(exception);
	});

	return promise;
}

And calling it using:

ProcessUtils.ChmodAndExecute(serverPath)
.Then((ProcessUtils.ProcessResult processResult) => 
{
	// Do something here
})
.Catch(exception =>
{
	// ...
});

The thing is that, I never enter inside the // Do something here.
It's because I'm using return ExecuteProcess(filePathWithArguments); in the ChmodAndExecute method, and inside this ExecuteProcess method, I create ANOTHER promise.

One way I fixed it is by passing the already existing promise to my ExecuteProcess method, doing return ExecuteProcess(promise, filePathWithArguments); like below

public static IPromise<ProcessResult> ChmodAndExecute(string filePath)
{
	Promise<ProcessResult> promise = new Promise<ProcessResult>();

	ExecuteProcess("chmod", "+x \"" + filePath + "\"")
	.Then(processResult => 
	{
		return ExecuteProcess(promise, filePath);
	})
	.Catch(exception =>
	{
		promise.Reject(exception);
	});

	return promise;
}

But my question is... can I cast this ExecuteProcess method to my already created promise?
Doing something like:
promise = (Promise)ExecuteProcess(filePath); ?
which would give the following code:

public static IPromise<ProcessResult> ChmodAndExecute(string filePath)
{
	Promise<ProcessResult> promise = new Promise<ProcessResult>();

	ExecuteProcess("chmod", "+x \"" + filePath + "\"")
	.Then(processResult => 
	{
		promise = (Promise<ProcessResult>)ExecuteProcess(filePath);
		return promise;
	})
	.Catch(exception =>
	{
		promise.Reject(exception);
	});

	return promise;
}

I tried, but it still does not work :/
PS: Yes, my ExecuteProcess creates a new promise, but also returns it. So if I cast the result of ExecuteProcess, I should now have the new promise inside the promise variable...

Let me know if this is not clear, and thanks in advance!

Yohan

Is there a "mother" class common to every promises?

Hello again :)

I have these two methods:

    private IEnumerator TimeOutPromiseCoroutine(Promise promise, float timeOut)
    {
        yield return new WaitForSeconds(timeOut);

        if (promise.CurState == PromiseState.Pending)
        {
            promise.Reject(new Exception("Promise has timed out"));
        }
    }

    private IEnumerator TimeOutPromiseCoroutine(Promise<String[]> promise, float timeOut)
    {
        yield return new WaitForSeconds(timeOut);

        if (promise.CurState == PromiseState.Pending)
        {
            promise.Reject(new Exception("Promise has timed out"));
        }
    }

They are identical, except for the first parameter used.
Is there a way I can use only one method for both cases?

If this is not clear enough, please tell me, I'll explain it in more details.

Returning an "empty" promise?

Hey guys!
First of all, thanks a LOT for this super awesome library. Code is so much cleaner thanks to it!

Question:
I have something like that:

Picture.Exists()
.Then(exists =>
{
    if (exists)
    {
        return Picture.Download();
    }
    else
    {
        // What should I do here to finish the promise and not go to the next "Then"?
    }
})
.Then(byteArray =>
{
    // Do something with the byte array
});

My question is in the comment of the code.
If the file does not exists, I don't want to go to the next Then. And the compiler is asking me to return something but... what should I return?

Thanks a lot!

Finally promise resolves even if chain is rejected

To reproduce, consider the following promise chain:

new Promise().Then(() => {
    throw new Exception();
    })
    .Finally(() => {})
    .Then(() => Debug.Print("SUCCESS");
    .Catch(ex =>  Debug.Print("FAILURE");

In version 1.3.0 of this library, I'm seeing SUCCESS and I expect to see FAILURE. It looks like this is because the Finally method resolves the promise it returns in both the Then and Catch handler of the promise it's chaining to...that's not right, is it?

If no, I'm happy to fix and submit a PR.

How does a Promise return control to Unity?

One thing I'm confused about is returning control to Unity after calling a Promise that initiates a coroutine.

I would like my app to remain responsive while I perform a sequence of Promises that are encapsulated within a Coroutine, because the app's UI needs to update an element to indicate the progress of these operations.

Is it possible (or is this a good use case) for RSG.Promises ?
I'm not sure how to proceed, and any input you can provide would be greatly appreciated!

Multiple async operations cascade

Hello again! Sorry if I post a lot of issues that are just questions :/

I followed the ReadMe but can't find how to do something like this:
Picture.GetListFromId and Tag.GetListFromPicture are both promises.

Picture.GetListFromId(3)
.Then(pictureList =>
{
    for (int i = 0; i < pictureList.Count; i++)
    {
        return Tag.GetListFromPicture(pictureList[i]);
    }
})
.Then(tagList =>
{
});

What I want to achieve, is first get the list of pictures, and then with this list, I want to get the tags for each picture.
So if I have 3 pictures, each having 5 tags, it would:

  • retrieve the list of pictures
  • for each picture retrieve the tag list
  • and trigger the Then(tagList =>) for each picture, so I'll receive 3 time this callback with 3 different list of tags

I don't even know if this is possible...

Also in my example here, I would get a callback with the tagList, but I won't have the pictureList[i] variable that I passed (and I need it) :/

Thanks a lot!

Best way to add a time out?

Hey there :)

I don't think there is a builtin thing to add a time out to a promise, so how would you do it?
The only thing I can think of right now is after I start my promises, I just do something like TimeOutHelper.RegisterPromise(promise) that will start a coroutine, wait for x seconds, and then reject the coroutine if it's not done.

Thanks!

*** can only be called from the main thread

So if you make any interaction with gameObject in "then" it throws simular exception.

sample code:

        public void Start()
        {
            this.Label = gameObject.GetComponent<Text>();
            SOMEFUNCTION.Done(x => this.SetPlayerName());
        }

        private void SetPlayerName()
        {
            this.Label.text = "player name"; // the exception is on this line
        }

exception:

get_isActiveAndEnabled can only be called from the main thread.
Constructors and field initializers will be executed from the loading thread when loading a scene.
Don't use this function in the constructor or field initializers, instead move initialization code to the Awake or Start function.

Add Catch<TException> to IPromise and IPromise<TPromised> interface?

The only difference from a normal Catch is that if the exception does not match the expected type the onRejected handler is not called.

Code that looks like this:

Blackbox.ReturnsPromise()
  .Catch(ex => 
  {
      var specificException = ex as SpecificExceptionType;
      if(ex != null)
      {
          // Handle specific error
      }

      var otherSpecificException = ex as OtherSpecificExceptionType;
      if(otherSpecificException != null)
      {
          // Handle other specific error
      }

      // We didn't handle any of the specific exception types
  }).Then( data => 
  {
      // Do something useful
  });

Then can be written like this:

Blackbox.ReturnsPromise()
  .Catch<SpecificExceptionType>(ex => 
  {
      // Handle specific error
  })
  .Catch<OtherSpecificExceptionType>(ex => 
  {
      // Handle other specific error
  })
  .Catch(ex => 
  {
      // We didn't handle any of the specific exception types
  })
  .Then( data => 
  {
      // Do something useful
  });

The changes required to support this are small. Here is the implementation for the generic promise type.

public IPromise<PromisedT> Catch<TException>(Action<TException> onRejected) 
where TException : Exception
 {
    var resultPromise = new Promise<PromisedT>();
    resultPromise.WithName(Name);
    Action<PromisedT> resolveHandler = v =>
    {
        resultPromise.Resolve(v);
    };

    Action<Exception> rejectHandler = ex =>
    {
        var specificException = ex as TException;
        if (specificException != null)
        {
            onRejected(specificException);
        }

        resultPromise.Reject(ex);
    };

    ActionHandlers(resultPromise, resolveHandler, rejectHandler);

    return resultPromise;
}

Should throw custom exception types instead of ApplicationException

The Microsoft guidelines recommend against throwing ApplicationException, and throwing a custom type of exception would make it easier for users to distinguish between exceptions thrown by the library and their own code.

So far the only places where this would change anything is where we are attempting to resolve/reject a promise in an invalid state and when Race is called with no promises. If we merge #65 this will add one more case (where progress is updated on an already resolved/rejected promise).

Performance

Hello! This is a great library; thank you for sharing it with everyone.

I'm curious though -- have you guys tested this for mobile games? How is the performance? I haven't tried this library yet, but I imagine that this would take a toll on the GC?

Thanks!

Compare with async/await?

Seem like this project do the things that is very similar with MS async/await. So could you tell me what is the differences?

Promise after Catch should not resolve unless Catch handler returns a value

This is referring to the new functionality in v2.0. It looks like all Catch promises will now resolve, regardless of whether the Catch handler returns a value. I may be missing something, but if that's true I think this is inconsistent with the Promises/A+ spec. The Promises/A+ spec says the following about this (from https://promisesaplus.com/):

2.2.7 then must return a promise [3.3].

 promise2 = promise1.then(onFulfilled, onRejected);

2.2.7.1    If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).
2.2.7.2    If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.

Since onRejected doesn't have to return a value, 2.2.7.1 implies that you could have a situation where promise2 is not resolved. I think the ability to chain promises after a Catch that are not automatically resolved should be present in this Promise library as well.

Exception handling not consistent with A+

Consider the following JavaScript code:

const Promise = require('bluebird'); // Using native promises does not change the behavior

Promise.resolve()
  .then(() => {
    console.log('then1');
    throw new Error('Foo');
  })
  .catch(() => {
    console.log('catch');
  })
  .then(() => {
    console.log('then2');
  })

The output:

then1
catch
then2

Now, using RSG promises:

void Start () {
	 Promise.Resolved()
            .Then(() => {
                Debug.Log("Then1");
                throw new NotImplementedException("NO!");
            })
            .Catch((e) => {
		Debug.Log("Catch");
	    });
	    .Then(() => {
		Debug.Log("Then2");
            });
}

The output:

then1
catch

(Using v1.2.2)

Corresponding A+ spec rule: https://promisesaplus.com/#point-41

If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).

Attempt to resolve already rejected promise when combining All and Sequence

I've got a weird situation where I'm getting an exception when my code rejects an asynchronous promise (All) inside a synchronous block (Sequence). The code looks like this:

return GameController.Instance.ApplicationHandshake()
	.Then(() => GameController.Instance.GameHandshake())
	.Then(response => {
		return Promise.Sequence(
			() => Promise.All(
				ContentManager.Instance.LoadAssets(),
				GameController.Instance.Init(response)
					.Then(() => ContinueLoading())
			),
			StartGameInternal);
	});

The ContinueLoading method, which is the cause of the issue (although it seems as though it could also occur if LoadAssets throws an exception) looks like this:

private IPromise ContinueLoading()
{
	var storyUi = UIManager.Instance.SpawnPopup<StorySequenceUI>("ui/story/intro_sequence");
	return storyUi.Init()
		.Then(() => this.splashScreenGO.SetActive(true));
}

To test the issue, I'm explicitly Rejecting in an OnClick handler (we're using Unity) in StorySequenceUI. When I do this, the exception bubbles up properly, but I get a second exception as well:

System.ApplicationException: Attempt to resolve a promise that is already in state: Rejected, a promise can only be resolved when it is still in state: Pending`

Is this because Init in the first Sequence function has already rejected, but LoadAssets is trying to resolve? To be clear, in my case, I'm explicitly Rejecting inside a function called by the GameController.Instance.Init(response).Then(() => ContinueLoading()) block, but ContentManager.Instance.LoadAssets finishes after and resolves properly.

Is this a case where something like ThenRace would be better suited? Maybe I'm totally misunderstanding what is/should be happening here when one of the promises in an All block rejects but the others resolve.

Is there a "Always" callback?

Hello!

Quick question.
Is there a callback that is going to be called wether the "request" succeeded or not?

Let's say I'm starting a promise, and either it succeed or not, I want to call a specific method. And I don't want to have to call the method in "Then" and also in "Catch". Is there a way to do that?

Thanks!

Is .Done() equal to JS .finally()?

I've read through the source and don't think .Done(resolved, rejected) is equal to JavaScript's .finally(), is that correct? I know that .finally() is not officially in the spec, but almost all non-native Promise implementations offer the functionality and I think it'd be quite useful for game-development purposes as well.

In that case, would you mind adding a .Finally(Action) method? I'll see if I can create a pull request myself if it's not already on your agenda.

Best Regards
Philipp

IL2CPP Problems

Hi all, I've just started using Promises in Unity3d (2017.1) because they look.... promising. I've got a very small test scene running on PS4 compiling with il2cpp, stable .net 3.5, api compat .net 2.0 and no il2cpp optimisation set, the latest promise DLLs from here and I get errors at runtime with this code:

`public class PromiseTest : MonoBehaviour
{
private void Awake()
{
DoThing().Then(b => UnityEngine.Debug.Log("Done: "+b)).Done();
}

private Promise<bool> DoThing()
{
    Promise<bool> p = new Promise<bool>();
    p.Resolve(true);
    return p;
}

}`

Error at runtime:
MissingMethodException: No constructor found for System.Runtime.CompilerServices.StrongBox1[[RSG.Promise1+<>c__DisplayClass61[[System.Boolean, mscorlib, Version=2.0.0.0, Culture=, PublicKeyToken=b77a5c561934e089],[System.Boolean, mscorlib, Version=2.0.0.0, Culture=, PublicKeyToken=b77a5c561934e089]], RSG.Promise, Version=1.1.2.0, Culture=, PublicKeyToken=null]]::.ctor(RSG.Promise1+<>c__DisplayClass61[System.Boolean,System.Boolean])`

I've tried adding
<linker> <assembly fullname="System"> <namespace fullname="System.Runtime.CompilerServices" preserve="all"/> </assembly> <assembly fullname="RSG.Promise"> <namespace fullname="RSG.Promise" preserve="all"/> </assembly> <assembly fullname="RSG.Toolkit"> <namespace fullname="RSG.Utils" preserve="all"/> </assembly> </linker>

to a link.xml file, but still get the same problem. Does anyone know how this might be solved? I've love to use promises for my project.

Can't install with Unity v5.2.1f

Hi guys,

I trying to install with Unity v5.2.1f 64bit via nuget and got this error

PM> Install-Package RSG.Promise
Attempting to gather dependencies information for package 'RSG.Promise.1.1.6' with respect to project 'sk2-btf.CSharp', targeting '.NETFramework,Version=v3.5,Profile=Unity Subset v3.5'
Attempting to resolve dependencies for package 'RSG.Promise.1.1.6' with DependencyBehavior 'Lowest'
Resolving actions to install package 'RSG.Promise.1.1.6'
Resolved actions to install package 'RSG.Promise.1.1.6'
Install failed. Rolling back...
Package 'RSG.Promise 1.1.6' does not exist in project 'sk2-btf.CSharp'
Package 'RSG.Promise 1.1.6' does not exist in folder 'D:\bit\sk2-unity3d\src\sk2-btf\packages'
Install-Package : Could not install package 'RSG.Promise 1.1.6'. You are trying to install this 
package into a project that targets '.NETFramework,Version=v3.5,Profile=Unity Subset v3.5', but the 
package does not contain any assembly references or content files that are compatible with that 
framework. For more information, contact the package author.
At line:1 char:1
+ Install-Package RSG.Promise
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Install-Package], Exception
    + FullyQualifiedErrorId : NuGetCmdletUnhandledException,NuGet.PackageManagement.PowerShellCmdlets 
   .InstallPackageCommand

PM> 

Any hint to make it work please?

Thanks

Exceptions are not caught in Done (in Unity)

If you have a Promise that throws a null reference error in Done, it gets eaten and won't be visible in Unity.

e.g. If you have this field and leave it empty:
[SerializeField] UnityEngine.UI.Image _testImage

And then run a promise like this:

TestPromise()
.Catch(e => Debug.LogException(e))
.Done(_image.color = Color.red);

The body of Done would throw a null reference exception, but it never shows up in Unity's console and it silently stops execution.

"Resolve" a promise multiple times

Hello again,

Is there to "resolve" a promise multiple times?
Imagine I'm scanning for bluetooth devices, and I use a Promise for that.
I would like to call .Resolve(deviceAddress) every time a new address is found for example, and start another process that will reject the promise after 60sc (to stop scanning).
I don't think there's a way to do that, right?

ArgumentException on Catch method and (ab)use of Refletions

I am getting an ArgumentException on Catch at runtime when the promise is Resolved (I think)

ArgumentException: The field handle and the type handle are incompatible.
System.Reflection.FieldInfo.GetFieldFromHandle (RuntimeFieldHandle handle, RuntimeTypeHandle declaringType) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Reflection/FieldInfo.cs:171)
RSG.Promise1[UnityEngine.GameObject].Catch (System.Action1 onRejected) (at Assets/Lib/CSharpPromises/Promise.cs:431

The error occures on this line in the code

Argument.NotNull(() => onRejected);

and looking at the internals it uses scary machinery like

var value = parameter.Compile().Invoke();

which makes me wonder if this would run on iOS. Commenting the Argument.NotNull makes the code work fine. Why the use of reflection for a Promise library?

Support .NET Core

I tried to install RSG.Promise with .NET Core 1.0, but got this error:

Package RSG.Promise 1.2.0 is not compatible with netcoreapp1.0 (.NETCoreApp,Version=v1.0). Package RSG.Promise 1.2.0 supports: net35 (.NETFramework,Version=v3.5)

For Xamarin Forms?

Hi, I am trying to use your library in a Xamarin forms application but it does not allow me to add it to a PCL project.
There is no way to use it in this environment?
Thank you

Throwing errors from the Unhandled Exception callback?

Hi,

I am writing unit tests and would like to "expect" an Exception. I thought that if I added a handler for UnhandledException and threw an Exception in the handler it would propagate up into the test but it doesnt seem to, it seems to get caught within the Promise.

Any ideas how I may achieve this?

Cheers,
Mike

iOS issue with UnhandledException handler

I know CSharpPromise has not been tested on iOS. Here's one issue I hit (using the .dlls)

    void Awake() {
        Promise.UnhandledException += LogUnhandledException;
    }

    void LogUnhandledException(object sender, ExceptionEventArgs e)
    {
        Debug.LogErrorFormat("[NetworkManager] Promise had unhandled exception: {0} - {1}", sender, e.Exception);
        Debug.LogException(e.Exception);
    }
ExecutionEngineException: Attempting to JIT compile method '(wrapper managed-to-native) System.Threading.Interlocked:CompareExchange (System.EventHandler`1<RSG.ExceptionEventArgs>&,System.EventHandler`1<RSG.ExceptionEventArgs>,System.EventHandler`1<RSG.ExceptionEventArgs>)' while running with --aot-only.

  at RSG.Promise.add_UnhandledException (System.EventHandler`1 value) [0x00000] in <filename unknown>:0 
  at NetworkManager.Awake () [0x00000] in <filename unknown>:0 

I am on Unity5.1.3 and running on iOS 8

Exceptions in immediately called function?

Hi,

Suppose we have the following:

private IPromise DownloadFile()
{
    return DoAsyncOp1()
        .Then(x => DoAsynOp2())
        .Catch(e => Debugger.Log("Whoopsie!"));
}

private IPromise DoAsyncOp1()
{
    // If we throw an exception here, it wont be caught in the handler above
}

private IPromise DoAsynOp2()
{
    // If we throw an exception here, it WILL be caught in the handler above
}

Is the how would you engineer a safe solution to this? Would you do:

private IPromise DownloadFile()
{โ€‹
    return Promise.Resolve()
        .Then(() => DoAsyncOp1())
        .Then(x => DoAsynOp2())
        .Catch(e => Debugger.Log("Whoopsie!"));
}
private IPromise DoAsyncOp1()
{
    // If we throw an exception here, it will now be caught
}
private IPromise DoAsynOp2()
{
    // If we throw an exception here, it will also be caught
}

Or how would you structure your code? To be safe should you ALWAYS start every promise with Promise.Resolve() ?

Cheers,
Mike

When should we use Done?

Hey :)

Currently I'm using it like this:
Query
.Then
.Then
.Catch
for every request I have.

Is it ok? Or should I do:
Query
.Then
.Then
.Done
.Catch

I'm a little bit confused on what Done is use for, I don't really see the different between this an Then :/

Incorrect overloads of .Then and .Catch

There are currently 3 overloads of Promise<T>.Then that don't make sense and 1 of Promise.Then:

interface IPromise<PromisedT>
{
    // These should be changed to return a non-generic promise because the callbacks don't 
    // return a value
    IPromise<PromisedT> Then(Action<PromisedT> onResolved, Action<Exception> onRejected);
    IPromise<PromisedT> Then(Action<PromisedT> onResolved);

    // This should be changed to return IPromise<ConvertedT> to be more flexible.
    // Changing to ConvertedT would retain backwards compatibility because ConvertedT can be
    // the same as PromisedT
    // IPromise<PromisedT> Catch(Func<Exception, PromisedT> onRejected);
    // EDIT: this actually doesn't work - see below
}

interface IPromise
{
    // Now that onRejected handlers can return values, this no longer makes sense. The second
    // argument should be type 'Func<Exception, ConvertedT>' or 'Func<Exception, IPromise<ConvertedT>>'
    IPromise<ConvertedT> Then<ConvertedT>(Func<IPromise<ConvertedT>> onResolved, Action<Exception> onRejected);
}

These should be modified to fit the rest of the library or just removed.

In addition, there are many helpful overloads of .Then and .Catch that we could add to support use in a more similar way to JS Promises:

// Generic promise
IPromise<ConvertedT> Then(Func<PromisedT, ConvertedT> onResolved, Func<Exception, ConvertedT> onRejected);
IPromise<ConvertedT> Then(Func<PromisedT, IPromise<ConvertedT>> onResolved, Func<Exception, ConvertedT> onRejected);
IPromise<ConvertedT> Then(Func<PromisedT, ConvertedT> onResolved, Func<Exception, IPromise<ConvertedT>> onRejected);
IPromise<PromisedT> Catch(Func<Exception, IPromise<PromisedT>> onRejected);
IPromise Then(Action<PromisedT> onResolved);
IPromise Then(Action<PromisedT> onResolved, Action<Exception> onRejected);
IPromise Then(Action<PromisedT> onResolved, Func<Exception, IPromise> onRejected);
IPromise Then(Func<PromisedT, IPromise> onResolved, Func<Exception, IPromise> onRejected);
IPromise Catch(Func<Exception, IPromise> onRejected);

// Non-generic promise
IPromise<ConvertedT> Then(Func<ConvertedT> onResolved);
IPromise<ConvertedT> Then(Func<ConvertedT> onResolved, Func<Exception, ConvertedT> onRejected);
IPromise<ConvertedT> Then(Func<IPromise<ConvertedT>> onResolved, Func<Exception, ConvertedT> onRejected);
IPromise<ConvertedT> Then(Func<ConvertedT> onResolved, Func<Exception, IPromise<ConvertedT>> onRejected);
IPromise<ConvertedT> Then(Func<IPromise<ConvertedT>> onResolved, Func<Exception, IPromise<ConvertedT>> onRejected);
IPromise Then(Action onResolved, Func<Exception, IPromise> onRejected);
IPromise Then(Func<IPromise> onResolved, Func<Exception, IPromise> onRejected);
IPromise Catch(Func<Exception, IPromise> onRejected);

Most of these could be implemented quite easily by simply making them call through to the existing methods.

Using waitfor and promiseTimer update

Hi I've seen the article on what can go wrong. I'm wanting to replace my StartCorutine that has a yeild waitforseconds in a lateupdate. with Waitfor.

I'm having difficulties understanding how to implement this properly with the example of timed promises in the article.

First is this a mistake? should it be called WaitFor, why is this local function it named after the built in function name?

PromiseTimer promiseTimer = new PromiseTimer();

IPromise WaitFor(float timeToWait)
{
    return promiseTimer.WaitFor(timeToWait);
}

So I have a _promiseTimer.Update(Time.timeDelta) in LateUpdate, I understand that this updates the timer inside PromiseTimer but is it needed? What I am confused with, where to call my waitFor. I don't have anything to watch except time passed. I want to achieve with my promise, after x secs go do the function in the waitfor. Do i just put promise.WairFor(seconds) in LateUpdate.
I know I can assign a promise as a return to the corutine, should I use that approach instead of removing my existing corutine?

thanks

Applying the "try-catch-finally"/"using" patterns

I started using Promises to model asynchronous/pipelined execution flows in our game and I'm really impressed, it just works as I would expect! As I use it e.g. with I/O, I'd like to start a discussion on how to extend the library to work better with C# constructs like "using" and "try-catch-finally".

Try/Catch works (but the Catch method is violating A+ as it cannot return a value/promise) but I struggle to create a good "finally" case. Consider this:

public IPromise Store() 
{
    return Promise.Resolved().Then(() =>
    {
        memoryStream ms = new MemoryStream();
        return StoreImpl(ms);
    });
}

private IPromise StoreImpl(MemoryStream ms)
{
    return Promise.Resolved().Then(() => 
    {
        throw new Exception("That did not work as expected");
    });
}

How would Store() dispose of the MemoryStream when an exception occurs? A Then() callback would not suffice, it would work when the preceding promise resolves, but when it rejects it won't. The disposing code could be duplicated and added as Then() and Catch() callbacks, but by specifying a rejection callback at this point, the exception will not be passed to the client-side error handling. I could explicitely re-throw the exception in a Catch() callback, but that would not preserve the exception per se. The only way I could think of would be to duplicate the disposal code and return another rejecting promise with the same exception, which is currently not possible because the Catch() callbacks can not return a promise (violating the A+ spec):

IPromise Store() 
{
    return Promise.Resolved().Then(() =>
    {
        memoryStream ms = new MemoryStream();

        return StoreImpl(ms)
            .Catch((ex) => {
                ms.Dispose();
                // Currently cannot do this, the rejection handler can not return values but it should
                return Promise.Reject(ex);
            })
            .Then(() => {
                ms.Dispose();
                return Promise.Resolved();
            });
    });
}

What do you think about this? I'll maybe try to fork and fix the Catch() handling myself, but I'd also like to get some feedback on this issue first. Also I'd like to know if there are any other suggestions how finally can be supported more fluently and maybe even how using could be supported in any way!

Suggestion for finally:

  • Implement a IPromise Promise.Finally(Func<IPromise> callback) instance method
  • The callback type of finally does not take any parameters (neither value nor exception!)
  • The Finally callback can return a new promise
  • Finally itself returns a promise
  • The finally callback just sits transparently in the flow of execution, all values/exceptions are passed "around" it and given to the chained handlers

Suggestion for using:

  • Implement static method IPromise Promise.Using(IDisposable, Func<IPromise>)
  • The IDisposable is disposed off when the promise returned by the callback is resolved/rejected, via the new Finally() interface

Basically implemented like this:

static IPromise Using(IDisposable d, Func<IPromise> cb)
{
    return Promise.Resolved().Then(() => {
        return cb();
    }).Finally(() => {
        d.Dispose();
    });
}

I'd love to see this library going forward, as we won't get 4.5 features in unity in the near future and this is a very good alternative!

Possible issue when using PomiseTimer.WaitUntil for chains of identical user input

Just started messing around with this library and I believe I have encountered a bug. It's also possible that I am simply misusing the PromiseTimer class.

I have a function that waits for a specified key input from the user and then returns a string

private IPromise<string> WaitForKey(KeyCode inKey)
	{
		Promise<string> promise = new Promise<string>();

		pTimer.WaitUntil(_ => Input.GetKeyDown(inKey))
			.Then(() => promise.Resolve("I just pressed " + inKey.ToString()));

		return promise;
	}

This works as expected as long as I chain together different inputs. but if I ask for the same input twice in a row, they both resolve in the same frame. In the example below all three logs would occur after pressing spacebar a single time.

WaitForKey(KeyCode.Space)
			.Then(result => Debug.Log(result))
			.Then(result => WaitForKey(KeyCode.Space))
			.Then(result => Debug.Log(result))
			.Then(result => Debug.Log("We are all done taking input!!!"));

Is this expected behaviour? If so what would be the proper approach to this example?

Benefit of calling both Catch() and Done()?

This isn't so much an issue as a general question (sorry if there's a better place to put this, I wasn't sure). I notice that in examples 3, 4, and 5, you do the following pattern:

SomePromise
  .Then(() => {})
  .Catch(e => {})
  .Done();

Is there a benefit to calling both Catch and Done in these cases? I can see recovering from an exception in Catch and calling another Then, but the Done seems redundant.

Am I missing something?

Running Promises of different Types in Parallel

Is there any way to run Promises of different Types in parallel in the current version that I'm missing?
As it stands it seems like I'm only able to use Promise<T>.Allwith Promises of Type T.

I think it would be useful if there was an overload for Promise.All that took Promises of different types as arguments and returns a Promise<Tuple<T1, T2>> or something.

e.g.

var p1 = Promise<string>.Resolved("str");
var p2 = Promise<int>.Resolved(42);

Promise.All(p1, p2)
  .Done((Tuple<string, int> vals)=>{
    ...
  });

Run multiple promises, one after the other

Hello again :)

I have this bit of code:

IPromise[] promiseArray = new IPromise[packetCount];
for (int i = 0; i < packetCount; i++)
{
    promiseArray[i] = WriteCharacteristicInternal(byteArray.Skip(i * 20).Take(20).ToArray());
}

return Promise.All(promiseArray);

But as the doc says, it will run all my promises in parallel.
Also, it seems that even if I don't call WriteCharacteristicInternal, it will trigger the promise. So I don't really understand why in the doc it says that Promise.All will run them all in parallel, as they are already started just by calling the WriteCharacteristicInternal method.
Anyway :)
Is there a "native" way of saying that I want to run an array of promises in sequence? Waiting for one to finish, then start the other etc... without having to do that "manually" in multiple .Then?

Thanks!

Interrupting a promise sequence from the outside?

I'm trying to interrupt a promise sequence from the outside of the sequence.

I am launching some animations and behaviours that I would like to simply kill if a user decides to go back to the menu. How would I be able to achieve this?

Promise startPromise;

void Start() {
	Invoke ("Test", 0.1f);
	startPromise = Promise.Sequence(
		() => speechBubble.Say(truttaSpeech),
		() => Promise.All(
			ShowMenu(true),
			Promise.Sequence(
				() => phoneContainerUI.Grow (),
				() => phoneUI.Ring(MDRPhoneUI.VIDEO_PATH_STAGE2, 2, false, true)
			)
		)
	).Catch(exception => {
		Debug.Log("PROMISE INTERRUPT WAS CALLED");
	}).Then(() => {
		Debug.Log("PROMISE HAS COMPLETED SUCCESFULLY");
	}) as Promise;

}

void Test() {
	startPromise.Reject (new Exception("Invoke rejection test"));
}

Error when building for Hololens

After building my Unity project for the Hololens this packages breaks.

In the Unity editor it works fine. I included the dll in my assets/packages folder.
I'm using Unity 2017.3.1p1. I also tried installing the package via Nuget, but I still get an error.

Severity	Code	Description	Project	File	Line	Suppression State
Error	CS0012	The type 'IPromise<>' is defined in an assembly that is not referenced. You must add a reference to assembly 'RSG.Promise, Version=3.0.0.0, Culture=neutral, PublicKeyToken=7019fe0722eef3a4'.

UnhandledException event fired on promise chain with Catch handler

I'm hitting a snag that I think might be a bug, but it also may just be a misunderstanding on my part.

I have code that adds a handler to Promise UnhandledException event. With that in place, given this simple promise chain:

Promise.Resolved()
    .Then(() => {
        throw new Exception();
    })
    .Catch(ex => { })
    .Done();

I was expecting that the UnhandledException handler would fire because I call Catch, but that appears to not be the case; my UnhandledException handler is fired whether or not I include Catch in the chain. Should the exception stop at Catch? Or will the UnhandledException event always get fired?

I am using version 1.3.0, compiled from GitHub source.

To Coroutine

Hi,

Is there a way to convert a Promise to a Coroutine (to keep consistency with for legacy code)?

Cheers,
Mike

Promise method is not thread safe.

hi, I'm use the tools for my Unity project, but encounter some exception seems about thread safe problem.
A simple stage can reproduction it:
Simulating 10000 times Promise.Then<T> method inner ThreadPool by for statement will throw some exception at Promise<T>.InvokeResolveHandlers's resolveCallbacks [i] line (about at 326 lines), I have suffered array out of index and object is null.
I think add some lock for resolveCallbacks, resolveRejectables and rejectHandlers is a good idea.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.