我希望能够从F#中使用C#库。大多数情况下,这非常简单。但是,如果我尝试调用返回Task<T>
的函数,则无法获得返回值。
所以,我有C#方法,其定义如下:
public async Task<TEvent> ReadEventAsync<TEvent>(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
}
我将此函数部分应用于IEventStoreRepository
的实例以及我希望从以下位置检索事件的流名称:
let readEvent = readEventFromEventStore eventStore streamName
然后,最后,我应用剩下的参数:
let event = readEvent StreamPosition.Start
当我得到event
的值时,它是FSharpAsync<object>
,而不是我预期的T
中的Task<T>
。
调用用C#编写的async
方法的F#中返回类型为Task<T>
并访问T
的值的正确方法是什么?
答案 0 :(得分:15)
首先,在您的用例中,不需要async { }
块。 Async.AwaitTask
returns an Async<'T>
,因此您的async { }
块只是展开您获得的Async
对象并立即重新包装它。
现在我们已经摆脱了不必要的async
阻止,让我们看看你已经获得的类型,以及你想要获得的类型。你有一个Async<'a>
,你想要一个'a
类型的对象。浏览available Async
functions,具有Async<'a> -> 'a
类型签名的是Async.RunSynchronously
。它需要两个可选参数,一个int
和一个CancellationToken
,但是如果你把它们排除在外,你就得到了你正在寻找的功能签名。当然,一旦你查看文档,结果证明Async.RunSynchronously
是 F#等价于C#的那种(但不完全是) )就像C#的await
,这就是你想要的。await
一样。 C#的await
是一个可以在async
函数中使用的语句,而F#的Async.RunSynchronously
使用async
对象阻塞当前线程,直到该async
对象完成运行。在这种情况下,这正是您正在寻找的。 p>
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!
,所以最好写成:{/ p>
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
,{ {1}},StartImmediate
,StartAsTask
等等。 Async.StartImmediate
example也会对Async.SwitchToContext
function进行一些讨论,这可能是您想要阅读的内容。但我对StartWithContinuations
s不够熟悉,不仅仅是告诉你。
答案 1 :(得分:1)
在这种情况下使用library(data.table)
library(ggplot2)
library(scales)
library(dplyr)
pct <- function(x) {x/lag(x)-1}
Dates = seq(from = as.Date("2000-01-01"),
to =as.Date("2018-10-01"),
by = "1 month")
set.seed(1024)
this_raw = data.frame(CM = Dates,
value = rnorm(n = length(Dates)),
variable = rep("FAKE",length(Dates)))
this_diff = na.omit(as.data.table(this_raw %>%
group_by(variable) %>%
mutate_each(funs(pct), c(value))))
this_diff$type = "PerCng"
this_raw$type = "RAW"
plot_all = rbindlist(list(this_raw,this_diff))
plot_all$type = factor(plot_all$type, levels = c("RAW", "PerCng"))
out_gg = plot_all %>%
ggplot(aes(x=CM, y=value)) +
geom_line(color = "royalblue3") +
theme(legend.position='bottom')+
ggtitle("FAKE DATA") +
facet_wrap(~ type, scale = "free_y", nrow = 2,
strip.position = "left",
labeller = as_labeller(c(RAW = "Original", PerCng = "% Change") ) )+
scale_x_date(date_breaks = "12 month", date_labels = "%Y-%m",
date_minor_breaks = "3 month")+
ylab("")+
theme(plot.title = element_text(hjust = 0.5,size = 12),
axis.text.x = element_text(size = 6,angle = 45, hjust = 1),
axis.text.y = element_text(size = 6),
axis.title.y = element_text(size = 6)) +
theme(strip.background = element_blank(),
strip.placement = "outside")+
theme(legend.title=element_blank())
print(out_gg)
计算表达式(F#调用基于C#Task的XxxAsync方法)的另一种方法是使用来自以下方面的async
计算表达式:
https://github.com/rspeele/TaskBuilder.fs
Giraffe F#Web框架出于相同的原因而使用task
:
https://github.com/giraffe-fsharp/Giraffe/blob/develop/DOCUMENTATION.md#tasks