首页 > 解决方案 > F#异步中的结果与提高?

问题描述

似乎有两种方法可以在async工作流中返回错误:raiseResult.

let willFailRaise = async {
  return raise <| new Exception("oh no!")
}

let willFailResult = async {
  return Result.Error "oh no!"
}

对于调用者,处理有点不同:

async {
  try 
    let! x = willFailRaise
    // ...
  with error -> 
    System.Console.WriteLine(error)
}

async {
  let! maybeX = willFailResult
  match maybeX with
  | Result.Ok x -> 
    // ...
  | Result.Error error -> 
    System.Console.WriteLine(error)
}

我的问题是:

标签: f#async-await

解决方案


这取决于我们在谈论什么样的错误。基本上分为三种:

  • 域错误(例如,用户提供了无效数据、使用此电子邮件的用户已注册等)
  • 基础设施错误(例如,您无法连接到另一个微服务或数据库)
  • 由程序员的错误引起的恐慌(例如NullReferenceException或等等)。StackOverflowException

虽然这两种方法都可以完成工作,但通常您应该关心的应该是使您的代码尽可能地自记录且易于阅读。这意味着以下内容:

  • 域错误:绝对选择Result. 这些“错误”是意料之中的,它们是您工作流程的一部分。UsingResult在函数的签名中反映了您的业务规则,这非常有用。
  • 基础设施故障:视情况而定。如果您有微服务,那么可能会出现这些故障,并且使用起来可能会更方便Result。如果没有 - 去寻找例外。
  • 恐慌:绝对Exception。首先,您不能用覆盖所有内容Result,无论哪种方式,您都需要全局异常过滤器。第二件事——如果你试图覆盖所有可能的恐慌——代码会非常快地变成一场令人讨厌的灾难,这将扼杀使用Result域错误的全部意义。

Async所以实际上这与C# 互操作无关,它与代码的可读性和可维护性有关。至于C# iterop——别担心,Result有所有的方法来帮助,比如IsError等等。但是你总是可以添加一个扩展方法:

[<AutoOpen>] module Utils = type Result<'Ok, 'Error> with member this.Value = match this with | Ok v -> v | Error e -> Exception(e.ToString()) |> raise


推荐阅读