首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >异步编程,异步在asycns

异步编程,异步在asycns
EN

Stack Overflow用户
提问于 2022-07-29 11:26:08
回答 1查看 45关注 0票数 1

我有一个DU (不要担心特定的类型,但是有两个普通选项,或者一些错误)

代码语言:javascript
复制
type Content = 
    | Episode of EpisodeJSON.Root
    | Program of string
    | Errors of List<exn>

我有两个函数(同样,细节并不重要,只是类型):

代码语言:javascript
复制
let getEpisode : _ -> _ -> Async<Content> = ...

let getProgram : _ -> _ -> Async<Content> = ...

我想写

代码语言:javascript
复制
let getContent : _ -> _ -> Async<Content> = 
   fun (contentBAPIClient: ContentBAPI.Client) id -> ...

这样,它尝试从getEpisode获取数据,如果有错误,尝试从getProgram获取数据,如果再次出现错误,则将两个错误作为错误DU返回。

所以,这是可行的

代码语言:javascript
复制
let getContent : _ -> _ -> Async<Content> = 
    fun (contentBAPIClient: ContentBAPI.Client) id -> 
        async {
            let! episodeMaybe = getEpisode contentBAPIClient id
            let! programMaybe = getProgram contentBAPIClient id

            return
                match episodeMaybe with
                | Errors xs -> 
                    match programMaybe with
                    | Errors ys -> 
                        Errors (List.append xs ys)
                    | program ->
                        program
                | episode -> 
                    episode
        }

但是我注意到即使数据是通过getepisode找到的,getprogram也在执行。

如何构造这个简单的函数来先尝试getEpisode,然后只在插曲“失败”时尝试getProgram

这个很好,还是有点笨重。

代码语言:javascript
复制
let getContent : _ -> _ -> Async<Content> = 
    fun (contentBAPIClient: ContentBAPI.Client) id -> 
        async {
            let! episodeMaybe = getEpisode contentBAPIClient id

            match episodeMaybe with
            | Errors xs -> 
                let! programMaybe = getProgram contentBAPIClient id
                match programMaybe with
                | Errors ys -> 
                    return Errors (List.append xs ys)
                | program ->
                    return program
            | episode -> 
                return episode
        }
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-07-31 19:47:32

如果您使用某些库来处理表示可能失败的异步计算的值,即类型Async<Result<'TOk, 'TErrror>>,这将变得容易得多。

一个不错的选择应该是类似于FsToolkit.ErrorHandling的东西。这定义了asyncResult计算表达式以及一些您可能认为有用的原语。

为了让它编译,我从以下几个方面开始:

代码语言:javascript
复制
#r "nuget: FsToolkit.ErrorHandling"
open FsToolkit.ErrorHandling

type Client = class end 

let getEpisode contentBAPIClient id = 
  async { return Error ["no episode"] }

let getProgram contentBAPIClient id = 
  async { return Ok "fine" }

现在,要做(我认为)你正在做的事情,你可以使用:

代码语言:javascript
复制
let getContent (contentBAPIClient: Client) id = 
  getEpisode contentBAPIClient id
  |> AsyncResult.orElseWith (fun e1 ->
      getProgram contentBAPIClient id
      |> AsyncResult.mapError (fun e2 -> e1 @ e2) )

在这里,我们试图返回插曲,但如果没有,我们尝试获得程序(使用orElseWith)。唯一棘手的是,我们需要传递来自第一个调用的错误,然后使用mapError将这些错误附加到第二个调用的(潜在)错误中。

请注意,您还可以编写:

代码语言:javascript
复制
let getContent (contentBAPIClient: Client) id = 
  asyncResult {
    let! ep = getEpisode contentBAPIClient id
    and! prog = getProgram contentBAPIClient id
    return ep, prog }

这只有在两个调用都成功的情况下才能成功,因此它在许多场景中可能非常有用,但并不能完全完成您想要做的事情。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73165971

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档