当前位置:  开发笔记 > 编程语言 > 正文

正确等待F#异步C#方法,返回类型为Task <T>

如何解决《正确等待F#异步C#方法,返回类型为Task<T>》经验,为你挑选了1个好方法。

我希望能够从F#中使用C#库.大多数情况下,这非常简单.但是,如果我尝试调用返回的函数,则Task无法获取返回值.

所以,我有C#方法,其定义如下:

public async Task ReadEventAsync(string streamName, int position) where TEvent: class

我试图从F#中使用此方法,如下所示:

let readEventFromEventStore<'a when 'a : not struct> (eventStore:IEventStoreRepository) (streamName:string) (position:int) = 
     async {
            return eventStore.ReadEventAsync(streamName, position) 
            |> Async.AwaitTask
            }

我部分地应用此函数与an的实例IEventStoreRepository和我希望从以下位置检索事件的流名称:

let readEvent = readEventFromEventStore eventStore streamName

然后,最后,我应用剩余的参数:

let event = readEvent StreamPosition.Start

当我得到的值event是一个FSharpAsync,而不是TTask我的预期.

调用asyncC#编写的方法的F#中的正确方法是什么,返回类型为?Task并且访问T?的值?



1> rmunn..:

首先,在您的用例中,不需要async { }块.Async.AwaitTask返回一个Async<'T>,所以你的async { }块只是打开它Async你得到对象并立即重新包装它.

现在我们已经摆脱了不必要的async阻止,让我们来看看你得到的类型,以及你想要获得的类型.你有一个Async<'a>,你想要一个类型的对象'a.查看可用的Async函数,具有类型签名的那个Async<'a> -> 'aAsync.RunSynchronously.它需要两个可选参数,a int和a CancellationToken,但是如果你把它们留下来,你就得到了你正在寻找的功能签名.当然,一旦你看到文档就会发现它Async.RunSynchronously就是这样相当于C#的F# await,这就是你想要的.和C#一样(但不完全一样)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

这应该可以帮到你找到你想要的东西.并注意找出所需函数的函数签名的技术,然后查找具有该签名的函数.它将来会有很多帮助.

更新:感谢您Tarmil在意见指出我的错误:Async.RunSynchronously不是等同于C#的await.它非常相似,但是由于RunSynchronously阻止了当前线程,因此需要注意一些重要的细微之处.(您不想在GUI线程中调用它.)

更新2:如果要在不阻塞当前线程的情况下等待异步结果,它通常是模式的一部分,如下所示:

    调用一些异步操作

    等待结果

    做一些结果

编写该模式的最佳方法如下:

let equivalentOfAwait () =
    async {
        let! result = someAsyncOperation()
        doSomethingWith result
    }

上面假设doSomethingWith返回unit,因为你是因为它的副作用而调用它.如果它返回一个值,你会做:

let equivalentOfAwait () =
    async {
        let! result = someAsyncOperation()
        let value = someCalculationWith result
        return value
    }

或者,当然:

let equivalentOfAwait () =
    async {
        let! result = someAsyncOperation()
        return (someCalculationWith result)
    }

这假设someCalculationWith不是异步操作.相反,你需要将两个异步操作链接在一起,其中第二个使用第一个结果 - 或者甚至是某种序列中的三个或四个异步操作 - 那么它看起来像这样:

let equivalentOfAwait () =
    async {
        let! result1 = someAsyncOperation()
        let! result2 = nextOperationWith result1
        let! result3 = penultimateOperationWith result2
        let! finalResult = finalOperationWith result3
        return finalResult
    }

除了let!后面的return内容完全相同return!,所以写得更好:

let equivalentOfAwait () =
    async {
        let! result1 = someAsyncOperation()
        let! result2 = nextOperationWith result1
        let! result3 = penultimateOperationWith result2
        return! (finalOperationWith result3)
    }

所有这些函数都将产生一个Async<'T>,其中'T将是async块中最终函数的返回类型.要真正运行的异步块,你要么Async.RunSynchronously如已经提到的,或者你可以使用的各种一个Async.Start功能(Start,StartImmediate,StartAsTask,StartWithContinuations,等).这个Async.StartImmediate例子也谈到了这个Async.SwitchToContext函数,这可能是你想要阅读的内容.但我不太熟悉SynchronizationContexts告诉你更多.


呃,这里有一些错误的信息.也就是说,`Async.RunSynchronously`肯定是*不是F#相当于C#的`await`.它更像是C#的`task.Wait()`,即它阻塞当前线程直到任务完成.`as!`在`async {}`块中是F#相当于`await`.
推荐阅读
拾味湖
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有