Promise equivalent in C#

2020-02-16 05:47发布

In Scala there is a Promise class that could be used to complete a Future manually. I am looking for an alternative in C#.

I am writing a test and I want it to look it similar to this:

// var MyResult has a field `Header`
var promise = new Promise<MyResult>;

handlerMyEventsWithHandler( msg =>
    promise.Complete(msg);
);

// Wait for 2 seconds
var myResult = promise.Future.Await(2000);

Assert.Equals("my header", myResult.Header);

I understand that this is probably not the right pattern for C#, but I couldn't figure out a reasonable way to achieve the same thing even with somewhat different pattern.

EDIT: please note, that async/await doesn't help here, as I don't have a Task to await! I just have an access to a handler that will be run on another thread.

5条回答
▲ chillily
2楼-- · 2020-02-16 06:18

The rough C# equivalent without third-party libraries would be:

// var MyResult has a field `Header`
var promise = new TaskCompletionSource<MyResult>();

handlerMyEventsWithHandler(msg =>
  promise.SetResult(msg)
);

// Wait for 2 seconds
if (promise.Task.Wait(2000))
{
  var myResult = promise.Task.Result;
  Debug.Assert("my header" == myResult.Header);
}

Note that it is usually best to use the await/async to as high a level as possible. Accessing the Result of a Task or using Wait can in some cases introduce deadlocks.

查看更多
不美不萌又怎样
3楼-- · 2020-02-16 06:33

In C#:

  • Task<T> is a future (or Task for a unit-returning future).
  • TaskCompletionSource<T> is a promise.

So your code would translate as such:

// var promise = new Promise<MyResult>;
var promise = new TaskCompletionSource<MyResult>();

// handlerMyEventsWithHandler(msg => promise.Complete(msg););
handlerMyEventsWithHandler(msg => promise.TrySetResult(msg));

// var myResult = promise.Future.Await(2000);
var completed = await Task.WhenAny(promise.Task, Task.Delay(2000));
if (completed == promise.Task)
  ; // Do something on timeout
var myResult = await completed;

Assert.Equals("my header", myResult.Header);

The "timed asynchronous wait" is a bit awkward, but it's also relatively uncommon in real-world code. For unit tests, I would just do a regular asynchronous wait:

var promise = new TaskCompletionSource<MyResult>();

handlerMyEventsWithHandler(msg => promise.TrySetResult(msg));

var myResult = await promise.Task;

Assert.Equals("my header", myResult.Header);
查看更多
戒情不戒烟
4楼-- · 2020-02-16 06:36

This is the old school way of doing Promises.
Back then i believe it was called synchronization :)

MyResult result = null;
var are = new AutoResetEvent(false);

handlerMyEventsWithHandler( 
    msg => {result = msg; are.Set();}
);

// Wait for 2 seconds
if(!are.WaitOne(2000)) {/* handle timeout... */}

Assert.Equals("my header", myResult.Header);

Just for Completeness - to large for a comment.
I agree with Stephen Cleary's answer.

But if you are building a facade around some legacy code this can be used to wrap old API's in a Task like:

public Task<MyResult> GetResultAsync() {
    MyResult result = null;
    var are = new AutoResetEvent(false);
    handlerMyEventsWithHandler(msg => {
        result = msg;
        are.Set();
    });
    are.WaitOne();
    return Task.FromResult(result);
}
查看更多
forever°为你锁心
5楼-- · 2020-02-16 06:37

Try looking into the async model. Tasks are the nearest equivalent in c#.

Here's a link to an MS Article explaining their use.

查看更多
Rolldiameter
6楼-- · 2020-02-16 06:39

You can use C# Promises library

Open sourced on Github: https://github.com/Real-Serious-Games/C-Sharp-Promise

Available on NuGet: https://www.nuget.org/packages/RSG.Promise/

查看更多
登录 后发表回答