C # 中的承诺等价物

在 Scala 中有一个专门用于手动完成 Future 的  。我正在寻找 C # 的替代方案。

我正在写一个测试,我希望它看起来像这样:

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

我知道这可能不是 C # 的正确模式,但是即使使用稍微不同的模式,我也不能找到一种合理的方法来实现同样的事情。

编辑: 请注意,async/await在这里没有帮助,因为我没有任务要等待!我只能访问另一个线程上运行的处理程序。

88603 次浏览

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

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

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/

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.

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

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

You can download future(https://www.nuget.org/packages/Future/) package from Nuget and can be used as below

        Promise<int> promise = new Promise<int>();
new Task(() =>
{
Thread.Sleep(100);
promise.Set(20);


}).Start();
int result=promise.Get();

As per the example you can create a promise object and do a get to get result, get will wait until result is on the object. You do a set from another thread as show in the above example.

This package provides the below two classes

  1. Promise : Which waits indefinitely for the result

  2. TimedPromise : Which waits for the result only till the specified time. If result is not available with in the time, it throws timeout exception