Dav*_*wer 15 c# f# asynchronous async-await
我希望能够从F#中使用C#库.大多数情况下,这非常简单.但是,如果我尝试调用返回的函数,则Task<T>无法获取返回值.
所以,我有C#方法,其定义如下:
public async Task<TEvent> ReadEventAsync<TEvent>(string streamName, int position) where TEvent: class
Run Code Online (Sandbox Code Playgroud)
我试图从F#中使用此方法,如下所示:
let readEventFromEventStore<'a when 'a : not struct> (eventStore:IEventStoreRepository) (streamName:string) (position:int) =
async {
return eventStore.ReadEventAsync(streamName, position)
|> Async.AwaitTask
}
Run Code Online (Sandbox Code Playgroud)
我部分地应用此函数与an的实例IEventStoreRepository和我希望从以下位置检索事件的流名称:
let readEvent = readEventFromEventStore eventStore streamName
Run Code Online (Sandbox Code Playgroud)
然后,最后,我应用剩余的参数:
let event = readEvent StreamPosition.Start
Run Code Online (Sandbox Code Playgroud)
当我得到的值event是一个FSharpAsync<object>,而不是T从Task<T>我的预期.
调用asyncC#编写的方法的F#中的正确方法是什么,返回类型为?Task<T>并且访问T?的值?
rmu*_*unn 15
首先,在您的用例中,不需要async { }块.Async.AwaitTask返回一个Async<'T>,所以你的async { }块只是打开它Async你得到对象并立即重新包装它.
现在我们已经摆脱了不必要的async阻止,让我们来看看你得到的类型,以及你想要获得的类型.你有一个Async<'a>,你想要一个类型的对象'a.查看可用的Async函数,具有类型签名的那个Async<'a> -> 'a是Async.RunSynchronously.它需要两个可选参数,a int和a CancellationToken,但是如果你把它们留下来,你就得到了你正在寻找的功能签名.当然,一旦你看到文档就会发现它Async.RunSynchronously就是这样相当于C#的F# 和C#一样(但不完全一样)await,这就是你想要的.await.C#await是一个可以在async函数内部使用的语句,而F#使用Async.RunSynchronously一个async对象阻塞当前线程,直到该async对象完成运行.在这种情况下,这正是您正在寻找的.
let readEventFromEventStore<'a when 'a : not struct> (eventStore:IEventStoreRepository) (streamName:string) (position:int) =
eventStore.ReadEventAsync(streamName, position)
|> Async.AwaitTask
|> Async.RunSynchronously
Run Code Online (Sandbox Code Playgroud)
这应该可以帮到你找到你想要的东西.并注意找出所需函数的函数签名的技术,然后查找具有该签名的函数.它将来会有很多帮助.
更新:感谢您Tarmil在意见指出我的错误:Async.RunSynchronously是不是等同于C#的await.它非常相似,但是由于RunSynchronously阻止了当前线程,因此需要注意一些重要的细微之处.(您不想在GUI线程中调用它.)
更新2:如果要在不阻塞当前线程的情况下等待异步结果,它通常是模式的一部分,如下所示:
编写该模式的最佳方法如下:
let equivalentOfAwait () =
async {
let! result = someAsyncOperation()
doSomethingWith result
}
Run Code Online (Sandbox Code Playgroud)
上面假设doSomethingWith返回unit,因为你是因为它的副作用而调用它.如果它返回一个值,你会做:
let equivalentOfAwait () =
async {
let! result = someAsyncOperation()
let value = someCalculationWith result
return value
}
Run Code Online (Sandbox Code Playgroud)
或者,当然:
let equivalentOfAwait () =
async {
let! result = someAsyncOperation()
return (someCalculationWith result)
}
Run Code Online (Sandbox Code Playgroud)
这假设someCalculationWith不是异步操作.相反,你需要将两个异步操作链接在一起,其中第二个使用第一个结果 - 或者甚至是某种序列中的三个或四个异步操作 - 那么它看起来像这样:
let equivalentOfAwait () =
async {
let! result1 = someAsyncOperation()
let! result2 = nextOperationWith result1
let! result3 = penultimateOperationWith result2
let! finalResult = finalOperationWith result3
return finalResult
}
Run Code Online (Sandbox Code Playgroud)
除了let!后面的return内容完全相同return!,所以写得更好:
let equivalentOfAwait () =
async {
let! result1 = someAsyncOperation()
let! result2 = nextOperationWith result1
let! result3 = penultimateOperationWith result2
return! (finalOperationWith result3)
}
Run Code Online (Sandbox Code Playgroud)
所有这些函数都将产生一个Async<'T>,其中'T将是async块中最终函数的返回类型.要真正运行的异步块,你要么Async.RunSynchronously如已经提到的,或者你可以使用的各种一个Async.Start功能(Start,StartImmediate,StartAsTask,StartWithContinuations,等).这个Async.StartImmediate例子也谈到了这个Async.SwitchToContext函数,这可能是你想要阅读的内容.但我不太熟悉SynchronizationContexts告诉你更多.
| 归档时间: |
|
| 查看次数: |
3282 次 |
| 最近记录: |