Flutter、Dart中优雅的使用 async await

我们在实际业务中,存在着复杂的异步依赖关系,传统的 then 和 await async 有着各种各样的缺陷,面对复杂的场景并不能很好的书写代码,这篇文章将带你解决 js 的孪生兄弟 dart 中复杂的异步操作,下面让我们开始吧!

信心满满

根据我们多年的JS经验,秒秒钟写个和JS差不多的不是手到擒来?开整

BaiduShurufa_2021-10-2_16-3-57.png

看起来是不是非常的nice?我们再给两个方法加上返回类型,再来试试?

BaiduShurufa_2021-10-2_16-19-10.png

why?

lMJG.jpg

怎么肥事?为什么会这样,no!!!

不得不说的dart坑爹的类型系统

我们先来看一下下面这段代码,大伙儿猜一下下面会输出啥?

image.png

先不急着揭晓答案,我们再修改一下代码,给list2再添加个double,大伙儿猜猜会输出啥?

image.png

不知道大家有没有猜到哈,现在我们揭晓答案

20170220594555_EpQChe.gif

image.png

为啥会这样??? 是不是感觉无比懵逼???

20201024509126_yYGMiO.gif

现在有请我们的工具人 @Alex 登场

Screenshot_20211002-223840.jpg

看到上面的对话是不是一脸问号?为啥我定义了 List<num> 不起作用?来着官方的答案就是:As design,dart设计如此,在经过推导转换之后,如果不直接声明泛型,返回的结果将直接以推导的类型为实际类型,并且直接变成运行时的类型,在前面声明类型直接无用。。。我就问一句,这个坑不坑,编译可以编译过,代码直接OJBK,跑的时候报错,这TM也没谁了。。。,建议大伙儿仔细研读上面的对话,免得编译一时爽,运行直接火葬场。。。

我们来看看 then 这个方法的定义,确实带有一个泛型,那么问题好办了

Future<R> then<R>(FutureOr<R> onValue(T value), {Function? onError});
复制代码

完善代码

经历过上面的求证,我们修改一下先前的代码

首先,如果我们要返回一个确定类型的列表,那么在 catchError 中返回 [err, null] 将会出现类型不匹配的问题

image.png

如果不考虑确切的类型,我们将列表修改为 List<dynamic> ,代码啥的全都跑起来了

image.png

但是这样存在一个问题,那就是没有类型提示,以及代码提示了,怎么说呢,dart 不支持 TS 的多种类型,挺好的。同时,取值并不能像js那样,直接解构列表,取值只能使用 .first .last 来取值,感觉是不是有点麻瓜呢?

这里和我之前JS有些差别,dart是一个强类型的语言,并且不像TS那样支持列表多类型,这里我们对返回结果有着明确的对象关联,我们直接封装一个对象来包装结果,有毛病吗?完全没毛病,这里不会出现JS中混乱的情况,并且包含正确的类型提示。

现在我们来设计一下,这个类应该包含成功的结果,错误的信息和堆栈,以及有个确切的值来表示是否发生错误,根据上面的内容,我们可以得到一个这样的类来包装我们的结果,看起来是不是很不错?

class Any<T> {
  final T? ok;
  final dynamic err;
  final dynamic stackTrace;

  bool get hasErr => err != null;

  Any.ok(this.ok, {this.err, this.stackTrace});

  Any.err(this.err, this.stackTrace, {this.ok});

  @override
  String toString() {
    return 'Any{ok: $ok, err: $err, stackTrace: $stackTrace}';
  }
}
复制代码

修改后完整的代码

import 'dart:async';

Future<void> main() async {
  var a = await any(err());
  print(a);
  print(a.runtimeType);
  print(a.hasErr);
  print(a.ok);
  print(a.ok.runtimeType);
  print(a.err);
  print('==============================');
  var b = await any(succ());
  print(b);
  print(b.runtimeType);
  print(b.hasErr);
  print(b.ok);
  print(b.ok.runtimeType);
  print(b.err);
  print('==============================');
  var c = await any(voidf());
  print(c);
  print(c.runtimeType);
  print(c.hasErr);
  // safe type check. void not any called.
  // print(c.ok);
  // print(c.ok.runtimeType);
  print(c.err);
}

Future<String?> err() async {
  throw Error();
}

Future<String> succ() async {
  return "success";
}

Future<void> voidf() async {
  print('void function.');
}

Future<Any<T?>> any<T>(Future<T> future) {
  return future.then<Any<T?>>((T t) {
    return Any.ok(t);
  }).catchError((err, stackTrace) {
    return Any.err(err, stackTrace, ok: null);
  });
}

class Any<T> {
  final T? ok;
  final dynamic err;
  final dynamic stackTrace;

  bool get hasErr => err != null;

  Any.ok(this.ok, {this.err, this.stackTrace});

  Any.err(this.err, this.stackTrace, {this.ok});

  @override
  String toString() {
    return 'Any{ok: $ok, err: $err, stackTrace: $stackTrace}';
  }
}
复制代码

我们来运行一下

image.png

是不是非常完美???后面再遇到像先前那样复杂的业务场景,抽离一下不同的业务,我们可以直接进行简单的判断,告别N层的嵌套哦

var d1 = await any(doSomething1());
if (d1.hasErr) {
  // do something..
  return;
}
print(d1.ok);
// do something..
var d2 = await any(doSomething2());
if (d2.hasErr) {
  // do something..
  return;
}
print(d2.ok);
// do something..
var d3 = await any(doSomething3());
if (d3.hasErr) {
  // do something..
  return;
}
// more task...
复制代码

总结一下

  • dart也是可以优雅的使用 asyncawait 的,只要进行简单的包装一下,完全OJBK
  • 时刻注意dart的类型系统,代码能编译,并不能代表能跑,嘻嘻
  • 有人可能好奇我为什喜欢用 any 这个词,而不是 to ,毕竟 Promise.any Future.any 已经有这个方法了。首先,我这儿不是一个两个元素的数组,然后,any 这个词也比较能表示出这里的返回不是正确就是错误,需要作出正确的选择

作者:尽管如此世界依然美丽
链接:https://juejin.cn/post/7014759220771815431
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

THE END