Asynchronous 我怎么能';丑';这些嵌套的异步部分,在F中#

Asynchronous 我怎么能';丑';这些嵌套的异步部分,在F中#,asynchronous,f#,Asynchronous,F#,首先,代码如下: let getCandlesFromAsync (exchange: IExchange) (instrument: Instrument) (interval: TimeSpan) (fromTime: DateTime) (toTime: DateTime) = async { let rec getAsync (c: CandleData list) (f: DateTime) (t: DateTime) = async {

首先,代码如下:

let getCandlesFromAsync (exchange: IExchange) (instrument: Instrument) (interval: TimeSpan) (fromTime: DateTime) (toTime: DateTime) =
    async {
        let rec getAsync (c: CandleData list) (f: DateTime) (t: DateTime) =
            async {
                //info $"requesting {instrument}: {f} - {t}"
                let! candles = exchange.GetCandlesAsync(instrument, interval, f, t)
                if candles.IsError then
                    return (failwith candles.GetError.Describe)
                else
                    //info $"received data {instrument}: {candles.Get.[0].Timestamp} - {candles.Get.[^0].Timestamp}"
                    let c = c @ candles.Get
                    if c.[^0].Timestamp < t - interval then
                        return! getAsync c (c.[^0].Timestamp + interval) t
                    else
                        return c
            }

        let cache = DataCache.getCache instrument
        let candlesFromCache = getCandlesFromCache cache interval fromTime toTime

        let firstTimestamp =
            match candlesFromCache.IsEmpty with
            | true  -> fromTime
            | false -> candlesFromCache.[^0].Timestamp + interval


        // check if we need some new data
        let downloadedCandles =
            async {
                if firstTimestamp < toTime then
                    let! x = getAsync [] firstTimestamp toTime
                    putCandlesInCache cache x
                    return x
                else
                    return []
            }

        let! d = downloadedCandles
        return candlesFromCache @ d
    }
让getCandlesFromAsync(exchange:IEExchange)(仪表:仪表)(间隔:TimeSpan)(fromTime:DateTime)(toTime:DateTime)=
异步的{
let rec getAsync(c:CandleData列表)(f:DateTime)(t:DateTime)=
异步的{
//信息$“正在请求{instrument}:{f}-{t}”
let!candles=exchange.getCandleAsync(仪表、间隔、f、t)
如果你是伊瑟罗那么
返回(failwith蜡烛。GetError。描述)
其他的
//info$“收到的数据{instrument}:{cands.Get.[0]。时间戳}-{cands.Get.[^0]。时间戳}”
让c=c@蜡烛。获取
如果c.[^0]。则时间戳fromTime
|false->candlesFromCache。[^0]。时间戳+间隔
//检查我们是否需要一些新数据
让下载的蜡烛=
异步的{
如果firstTimestamp
这段代码应该是从交易所下载价格蜡烛。它必须定期运行并跟上新数据

因为我需要一系列时间戳中的数据,所以我尝试缓存以前从exchange请求的数据。在不断前进的范围内,我只需检查范围内已有多少数据,以及需要获取多少数据

代码分为几个部分:

  • 从缓存中获取某个时间范围的数据的代码(此处未发布,但不相关)。它返回CandleData列表
  • 从exchange请求时间范围数据的代码(getAsync),它返回async
  • 一小段代码,用于确定缺少的内容并将这些部分粘在一起(函数的后半部分)
这里的问题是整个函数应该是异步的,但是getAsync是递归的,所以它有自己的异步块。 然后,将东西粘在一起的代码必须调用getAsync并将数据附加到来自缓存的数据,因此整个东西也包装在一个异步块中


必须有一个更干净的方法来做到这一点,但我不知道怎么做。欢迎提出任何建议

分离功能是最佳实践。它不一定会减少
async
s的数量,但它会使代码更干净、更容易理解

从exchange下载的功能可能是独立的:

let downloadFromExchange (exchange: IExchange) (instrument: Instrument) (interval: TimeSpan) (f: DateTime) (t: DateTime) =
    let rec getAsync (previousCandles: CandleData list) (f: DateTime) =
        async {
            //info $"requesting {instrument}: {f} - {t}"
            if f < t then return previousCandles else
            let! candles = exchange.GetCandlesAsync(instrument, interval, f, t)
            if candles.IsError then
                return (failwith candles.GetError.Describe)
            else
                //info $"received data {instrument}: {candles.Get.[0].Timestamp} - {candles.Get.[^0].Timestamp}"
                let c = previousCandles @ candles.Get
                match c.[^0].Timestamp + interval with
                | fr when fr < t -> return! getAsync c fr
                | _              -> return  c
        }
    getAsync [] f

我将条件
from
放在下载函数中,这样代码就更干净了。

所以将整个过程包装在一个异步中不是解决方案吗?我想有些语句打破了这个流程,我需要单独包装它们?
let getCandlesFromAsync exchange (instrument: Instrument) (interval: TimeSpan) (fromTime: DateTime) (toTime: DateTime) =
    async {
        let cache            = DataCache.getCache instrument
        let candlesFromCache = getCandlesFromCache cache interval fromTime toTime

        let firstTimestamp =
            match candlesFromCache.IsEmpty with
            | true  -> fromTime
            | false -> candlesFromCache.[^0].Timestamp + interval

        let! x = downloadFromExchange exchange instrument interval firstTimestamp toTime
        putCandlesInCache cache x
        return candlesFromCache @ x
    }