C# 中的 Promise 等价物

Posted

技术标签:

【中文标题】C# 中的 Promise 等价物【英文标题】:Promise equivalent in C# 【发布时间】:2016-12-24 02:37:12 【问题描述】:

在 Scala 中,有一个 Promise 类可用于手动完成 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 在这里没有帮助,因为我没有要等待的任务!我只是可以访问将在另一个线程上运行的处理程序。

【问题讨论】:

我想你在找Task&lt;T&gt; 【参考方案1】:

在 C# 中:

Task&lt;T&gt; 是未来(或Task 是返回单位的未来)。 TaskCompletionSource&lt;T&gt; 是一个承诺。

所以你的代码会这样翻译:

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

“定时异步等待”有点尴尬,但在实际代码中也相对不常见。对于单元测试,我只会做一个常规的异步等待:

var promise = new TaskCompletionSource<MyResult>();

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

var myResult = await promise.Task;

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

【讨论】:

有一种更好的方法可以使用取消令牌来实现超时。见***.com/q/23476576/1288449 @StevenLiekens:我同意超时通常更好地表示为取消令牌;但是,这对于使用超时取消操作的情况是最好的。在这种情况下,我们讨论的是取消 wait,而不是 操作,并且取消标记在这种情况下更尴尬。【参考方案2】:

没有第三方库的粗略 C# 等效项是:

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

请注意,通常最好将await/async 使用到尽可能高的级别。在某些情况下访问TaskResult 或使用Wait 可以introduce deadlocks。

【讨论】:

@Stephen 的答案是纯 C#。这里有什么不同。 @SahibKhan,1. 我在他之前发过帖子,2. 这也是纯 C#,尽管可能不再是最佳实践。【参考方案3】:

您可以使用 C# Promises 库

在 Github 上开源:https://github.com/Real-Serious-Games/C-Sharp-Promise

在 NuGet 上可用:https://www.nuget.org/packages/RSG.Promise/

【讨论】:

您可以在我的博客上阅读更多相关信息:what-could-possibly-go-wrong.com/promises-for-game-development【参考方案4】:

这是做 Promise 的老派方式。 那时我相信它被称为同步:)

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

仅出于完整性考虑 - 用于评论。我同意 Stephen Cleary's answer。

但是,如果您正在围绕一些遗留代码构建外观,这可以用于将旧 API 包装在 Task 中,例如:

public Task<MyResult> GetResultAsync() 
    MyResult result = null;
    var are = new AutoResetEvent(false);
    handlerMyEventsWithHandler(msg => 
        result = msg;
        are.Set();
    );
    are.WaitOne();
    return Task.FromResult(result);

【讨论】:

【参考方案5】:

尝试研究异步模型。任务是 c# 中最接近的等价物。

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

【讨论】:

【参考方案6】:

你可以从 Nuget 下载 future(https://www.nuget.org/packages/Future/) 包,可以如下使用

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

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

根据示例,您可以创建一个 promise 对象并执行 get 来获取结果,get 将等到结果出现在对象上。如上例所示,您可以从另一个线程执行一组操作。

这个包提供了以下两个类

    Promise : 无限期等待结果

    TimedPromise :仅在指定时间之前等待结果。如果没有及时得到结果,则抛出超时异常

【讨论】:

以上是关于C# 中的 Promise 等价物的主要内容,如果未能解决你的问题,请参考以下文章

RxJS 序列等价于 promise.then()?

原生 ES6 承诺中 Bluebird Promise.finally 的等价物是啥? [复制]

[AngularJS] AngularJS系列 进阶篇之promise

ES6深入浅出-9 Promise-3.Promise的细节

带你快速入门ES6中的Promise对象

在嵌套 forEach 中的 Promise.all 之前评估 Promise,导致 Promise.all 为空