Flutter、Dart中优雅的使用 async await
我们在实际业务中,存在着复杂的异步依赖关系,传统的 then
和 await async
有着各种各样的缺陷,面对复杂的场景并不能很好的书写代码,这篇文章将带你解决 js 的孪生兄弟 dart 中复杂的异步操作,下面让我们开始吧!
信心满满
根据我们多年的JS经验,秒秒钟写个和JS差不多的不是手到擒来?开整
看起来是不是非常的nice?我们再给两个方法加上返回类型,再来试试?
why?
怎么肥事?为什么会这样,no!!!
不得不说的dart坑爹的类型系统
我们先来看一下下面这段代码,大伙儿猜一下下面会输出啥?
先不急着揭晓答案,我们再修改一下代码,给list2再添加个double,大伙儿猜猜会输出啥?
不知道大家有没有猜到哈,现在我们揭晓答案
为啥会这样??? 是不是感觉无比懵逼???
现在有请我们的工具人 @Alex 登场
看到上面的对话是不是一脸问号?为啥我定义了 List<num>
不起作用?来着官方的答案就是:As design,dart设计如此,在经过推导转换之后,如果不直接声明泛型,返回的结果将直接以推导的类型为实际类型,并且直接变成运行时的类型,在前面声明类型直接无用。。。我就问一句,这个坑不坑,编译可以编译过,代码直接OJBK,跑的时候报错,这TM也没谁了。。。,建议大伙儿仔细研读上面的对话,免得编译一时爽,运行直接火葬场。。。
我们来看看 then 这个方法的定义,确实带有一个泛型,那么问题好办了
Future<R> then<R>(FutureOr<R> onValue(T value), {Function? onError});
复制代码
完善代码
经历过上面的求证,我们修改一下先前的代码
首先,如果我们要返回一个确定类型的列表,那么在 catchError
中返回 [err, null]
将会出现类型不匹配的问题
如果不考虑确切的类型,我们将列表修改为 List<dynamic>
,代码啥的全都跑起来了
但是这样存在一个问题,那就是没有类型提示,以及代码提示了,怎么说呢,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}';
}
}
复制代码
我们来运行一下
是不是非常完美???后面再遇到像先前那样复杂的业务场景,抽离一下不同的业务,我们可以直接进行简单的判断,告别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也是可以优雅的使用
async
、await
的,只要进行简单的包装一下,完全OJBK - 时刻注意dart的类型系统,代码能编译,并不能代表能跑,嘻嘻
- 有人可能好奇我为什喜欢用
any
这个词,而不是to
,毕竟Promise.any
Future.any
已经有这个方法了。首先,我这儿不是一个两个元素的数组,然后,any
这个词也比较能表示出这里的返回不是正确就是错误,需要作出正确的选择
作者:尽管如此世界依然美丽
链接:https://juejin.cn/post/7014759220771815431
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。