【问题标题】:Elegant error handling in Dart like Scala's `Try`Dart 中优雅的错误处理,如 Scala 的 `Try`
【发布时间】:2019-04-29 13:16:09
【问题描述】:

Dart 中的一个数据类:

import 'package:validate/validate.dart';
class AuthUser {
  final String email, token, username, bio, image;

  AuthUser(this.email, this.token, this.username, this.bio, this.image) {
    Validate.isEmail(this.email);
  }

  @override
  String toString() {
    return 'AuthUser{email: $email, token: $token, username: $username, bio: $bio, image: $image}';
  }
}

其中Validate.isEmail在匹配失败时会抛出错误:

static void matchesPattern(String input, RegExp pattern,[String message = DEFAULT_MATCHES_PATTERN_EX]) {
    if (pattern.hasMatch(input) == false) {
        throw new ArgumentError(message);
    }
}

static void isEmail(String input,[String message = DEFAULT_MATCHES_PATTERN_EX]) {
    matchesPattern(input,new RegExp(PATTERN_EMAIL),message);
}

现在我想用一种优雅的方式来新建这个类。 在使用 Scala 时,我可以使用 Try(new AuthUser(...)) 并对其进行模式匹配。

在 Dart 中,我首先尝试了 RxDart,

void main() {
  testWidgets('Counter increments smoke test', (WidgetTester tester) async {
    Observable.just(AuthUser("email", "token", "username", "bio", "img"))
        .doOnError((e, s) => print("oh no"))
        .listen((e) => print(e.toString()));
  });
}

不起作用,测试因错误而失败(这意味着 RxDart 根本没有捕获错误!!!)

我想试试Future,也失败了。

我想使用 dartz,但我很担心,因为只有一个维护者...

有什么建议吗?

【问题讨论】:

  • RxDart 没有捕捉到异常,因为异常被抛出之前它是Observable 的一部分。详细地说,您的代码相当于final user = AuthUser(); Observable.just(user);。如您现在所见,例外是在创建用户时。
  • dart 中没有模式匹配
  • @RémiRousselet 我知道,我只需要一个解决方案来优雅地尝试/捕获异常
  • @JRomero 所以有什么建议吗?
  • 我的第一反应就是不要。该语言具有捕获错误的特定方法,并且从另一种语言中移植您首选的方法可能不是最佳实践。不过这是个人意见。

标签: scala functional-programming dart flutter rx-java


【解决方案1】:

如果您可以使用 Future 这个建议有什么问题:Using Future.sync() to wrap your code?代码将如下所示:

void main() {
  var f = Future.sync(() {AuthUser("email", "token", "username", "bio", "img"); });
  f.then((v) =>  print("Value: " + v.toString())).catchError((e) => print("Failure: " +e.toString()));
}

主要技巧是Future.sync 有效地启用了参数的惰性求值,但您必须传递包装在函数中的参数。这实际上与 Scala 编译器对 Try (即按名称调用的参数)所做的相同技巧,但需要在周围添加几个括号。

【讨论】:

    【解决方案2】:

    如果您只想要根据是否发生异常返回任一类型的基本功能,那么您可以轻松创建一个实用程序类,如下所示。

    否则,我推荐@SergGr 关于使用Future.sync 的回答,因为它为您提供了更像管道的单子。

    void main() {
      Try<Error, void> result = Try.it(() => Validate.isEmail("test-example.com"));
    
      if (result is Success) {
        print("Good");
      } else if (result is Failure) {
        print("Error: " + result.exception().toString());
      }
    }
    
    typedef TryExec<V> = V Function();
    
    abstract class Try<E extends Error, V> {
    
      static Try<E, V> it<E extends Error, V>(TryExec<V> fn) {
        try {
          return Try.success(fn());
        } catch (e) {
          return Try.failure(e);
        }
      }
    
      static Try<E, V> failure<E extends Error, V>(Error e) {
        return new Failure(e);
      }
    
      static Try<E, V> success<E extends Error, V>(V v) {
        return new Success(v);
      }
    }
    
    class Failure<E extends Error, V> extends Try<E, V> {
      final E _e;
      Failure(this._e);
    
      E exception() => _e;
    }
    
    class Success<E extends Error, V> extends Try<E, V> { 
      final V _v;
      Success(this._v);
    
      V value() => _v;
    }
    

    【讨论】:

    • 我的第一反应就是不要。该语言具有捕获错误的特定方法,并且从另一种语言中移植您首选的方法可能不是最佳实践。不过这是个人意见。
    猜你喜欢
    • 2016-01-23
    • 2015-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-19
    • 2012-06-06
    • 2018-11-10
    • 2021-03-11
    相关资源
    最近更新 更多