[Flutter]Dart Future详解
在客户端的很多操作需要异步执行,使用callback处理异步,很容易写出回调地狱(Callback hell)代码,导致难以维护。所以在Web端出现了Promise
,Android有RxJava
,可以使用这些工具,将横向嵌套的callback代码改为纵向扩展,更符合人类的直觉,也更加容易维护。配合async,await更是能以同步的方式写异步代码。
Dart也同样提供了处理异步的工具Future
,和JavaScript中的Promise
十分相似,有Promise
使用经验的人使用Future
时会感到十分熟悉。
An object representing a delayed computation.
Future
是一个表示延迟计算的对象。代表一些计算将异步进行,并在将来获取到计算的结果。
Future有两种状态:Pending
和 Complete
。Pending
表示任务正在执行中,还未获取到结果;Complete
表示任务已经执行完,并且有结果。
结果有两种值:成功(succeed)或者失败(failed), 我们可以使用回调函数来获取结果值。
事件循环
和其他客户端开发框架类似(JS中的EventLoop, Android中的Looper,iOS中的RunLoop),Dart中也有一个无线循环的事件循环,包含两个队列: microtask队列
和event队列
。Dart会不断地循环访问这两个队列,从中取出任务来执行。microtask队列
的优先级高于event队列
,如果microtask队列
不为空,就从中取出任务执行。当microtask队列
为空时,才会访问event队列
。
![[Flutter]Dart Future详解](https://static.blogweb.cn/article/6289296bbc364729890bdeec01c7760f.webp)
通过scheduleMicrotask
方法可以向microtask队列
中添加任务,而UI事件、Timer等操作和Future的默认构造方法都是添加到event队列
中。
默认构造方法
Future构造方法
通过Future的默认构造方法可以创建一个Future对象,。默认构造方法的签名如下
factory Future(FutureOr<T> computation())
参数类型是FutureOr<T> computation()
,表示返回值是FutureOr<T>
类型的函数。可以根据该函数创建一个Future
对象。而FutureOr<T>
是一个union类型,代表参数函数的返回值,可以是T
类型,也可以是一个Future<T>
类型。
通过这个方法创建的Future
,computation
函数会被添加到event队列
中执行。
处理结果
then
创建完成Future
对象后,可以通过then
方法接收Future
的结果。
Future<R> then<R>(FutureOr<R> onValue(T value), {Function onError});
then
方法接收两个回调函数参数,onValue
接收成功回调,可选的onError
接收失败回调。
var future = Future(() {
//do compution
return "future value";
});
future.then((value) {
print("onValue: $value");
}, onError: (error) {
print("onError $error");
});
then
方法的返回值也是一个Future
,可以继续对这个对象调用then
方法,注册接收结果的函数。后注册then
函数接收的就是前一个then
函数的返回值,如果前一个函数没有返回值,后面就接收到null
值。
下面代码将会输出
onValue: future value
onValue: next value
var future = Future(() {
//do compution
return "future value";
});
future.then((value) {
print("onValue: $value");
return "next value";
}, onError: (error) {
print("onError $error");
}).then((value) {
print("onValue: $value");
});
then
方法的onValue
回调函数的返回值为FutureOr<R>
,代表该回调函数可以返回一个Future
对象,或者返回一个T
类型的值。如果是返回的是Future
对象,那么后面通过then
注册的回调函数将收到该Future
的结果。
下面代码将会输出
onValue: future value
onValue: request value
var future = Future(() {
//do compution
return "future value";
});
future.then((value) {
print("onValue: $value");
var otherFuture = Future(() {
//do request
return "request value";
});
return otherFuture;
}, onError: (error) {
print("onError $error");
}).then((value) {
print("onValue: $value");
});
通过这种方式 ,就可以发起多个异步网络请求,从上到下顺序执行,而不是通过callback进行横向嵌套。
catchError
当Future
没有正确的获取到结果发生异常时,除了在then
方法中注册onError
回调外,还可通过catchError
方法监听异常。
Future<T> catchError(Function onError, {bool test(Object error)});
下面代码将会输出 catchError Exception: exceptin occured
var future = Future(() {
//do compution
throw Exception("exception occured");
});
future.then((value){
print("onValue: $value");
}).catchError((error) {
print("catchError $error");
},);
catchError
方法还有一个可以选的test
函数参数。当发生异常时,会首先调用test
函数,如果该函数返回false,异常将不会被catchError
函数处理,会继续传递下去;如果test
函数返回ture,catchError
函数会处理该异常。如果未提供test
函数,默认处理为true。
通过then
方法的onError
参数和catchError
方法处理异常有什么不同呢?
这两种方式主要的区别是,通过catchError
方法可以捕获到前一个then
方法onValue
函数中的异常,而通过onError
函数方法,无法捕获同一个then
方法onValue
函数中的异常。
var future = Future(() {
return "future value";
});
future.then((value){
print("onValue: $value");
throw Exception("exception occured");
}, onError: (error) {
print("onError $error");
}).catchError((error) {
print("catchError $error");
});
上面代码将会输出
onValue: future value
catchError Exception: exception occured
异常被catchError
捕获,而onError
没有被调用。
whenComplete方法
Future对象还有一个whenComplete
方法,当该Future
处于完成状态时,通过该方法注册的回调会被调用,无论结果是成功还是失败,相当与finally
代码块。
whenComplete
方法的参数类型也是FutureOr
类型,返回值为Future<T>
类型。表示在该方法中可以返回一个值或者是Future
对象,在whenComplete
函数后,可以再调用then
、catchError
等方法,但是后面注册回调函数并不能获取到whenComplete
中返回的值,而是whenComplete
前的值。
该逻辑等效于下面的代码
Future<T> whenComplete(action()) {
return this.then((v) {
var f2 = action();
if (f2 is Future) return f2.then((_) => v);
return v
}, onError: (e) {
var f2 = action();
if (f2 is Future) return f2.then((_) { throw e; });
throw e;
});
}
timeout方法
Future<T> timeout(Duration timeLimit, {FutureOr<T> onTimeout()})
timeout
方法创建一个新的Future对象,接收一个Duration
类型的timeLimit
参数来设置超时时间。如果原Future
在超时之前完成,最终的结果就是该原Future
的值;如果达到超时时间后还未完成,就会产生TimeoutException
异常。
该方法有一个onTimeout
可选参数,如果设置了该参数,当发生超时时会调用该函数,该函数的返回值为Future的新的值,而不会产生TimeoutException
。
其他构造方法
sync构造方法
factory Future.sync(FutureOr<T> computation())
将会在当前task执行computation
计算,而不是将计算过程添加到任务队列中。
Future.micortask构造方法
factory Future.microtask(FutureOr<T> computation())
通过scheduleMicrotask
方法将computation函数添加到microtask队列
中,优先于event队列
执行。
factory Future.microtask(FutureOr<T> computation()) {
_Future<T> result = new _Future<T>();
scheduleMicrotask(() {
try {
result._complete(computation());
} catch (e, s) {
_completeWithErrorCallback(result, e, s);
}
});
return result;
}
下面代码将会输出 microtask future default fauture
Future(() {
print("default fauture");
});
Future.microtask(() {
print("microtask future");
});
value构造方法
factory Future.value([FutureOr<T> value]) {
return new _Future<T>.immediate(value);
}
通过value值创建一个Future
对象,vuale可以是一个确定的值或者是一个future对象。该方法使用了内部的immediate
函数,这个函数会使用scheduleMicrotask
在microtask队列
中更新Future的值。
error构造方法
factory Future.error(Object error, [StackTrace stackTrace])
通过error
对象和可选的stackTrace
创建Future
,可以使用该方法创建个一个状态为failed
的Future
对象。
delayed构造方法
factory Future.delayed(Duration duration, [FutureOr<T> computation()]);
创建一个延时执行的Future
对象,时间由duration
参数设置,到达指定事件后,将会执行computation
函数。如果computation
函数为空,Future
的值为null
。
wait静态方法
static Future<List<T>> wait<T>(Iterable<Future<T>> futures,{bool eagerError: false, void cleanUp(T successValue)})
类似于JS中Promise的all
方法。wait
静态方法可以等待多个Future执行完成,并通过List获取所有Future
的结果。如果其中一个Future
对象发生异常,会导致最终结果为failed。
该方法有两个可选参数,eagerError
和cleanUp
。
eagerError
默认值为false,当某一个Future
发生异常时,默认不会立刻使Future
处于failed状态,而是等待所有Future
都有结果后,改变状态为failed。如果设置为true,当其中一个Future
发生异常时,会立刻导致最终结果为failed,
如果设置了cleanUp
参数,当多个Future
中的一个发生异常时,其他成功的Future
的(非null)结果会传递给cleanUp
参数。如果没有发生异常cleanUp
函数不会被调用。
静态方法
any静态方法
static Future<T> any<T>(Iterable<Future<T>> futures)
类似于JS中Promise
的race方法。any静态方法可以多个Future
中第一个处于完成状态的Future
的结果,如果第一个完成的Future
处于成功状态,则新的Future
状态为成功。如果第一个完成的Future
处于失败状态,则新的Future状态为失败。其他Future的状态将被忽略。
wait
any
方法的参数都可以根据参数类型,推断出新的Future
类型。如果参数类型相同,则新的Future
类型于参数类型一致,如果不同,则为Object类型。
forEach静态方法
forEach方法有点像Stream的asycMap函数。
static Future forEach<T>(Iterable<T> elements, FutureOr action(T element))
forEach
静态方法可以遍历Iterable
中的每个元素执行一个操作,如果遍历操作返回的是Future
对象,则在该Future
完成后再进行下一次遍历,全部完成后返回null
。如果在某一次操作中发生异常,会停止遍历,最终的Future的状态为failed。
doWhile静态方法
static Future doWhile(FutureOr<bool> action())
doWhile
静态方法是 do while
循环的异步版本。可以一直执行一个action,直到action的值为false。
当循环结束时,doWhile
方法返回的Future的值也会是null(为什么要说也)。
总结
Future提供了丰富的Api来处理异步任务,除了这些Api外,还有其他工具可以配合使用。比如可以使用FutureBuilder
将Future的结果反应到UI界面上;可以使用asStream
方法将Future转换为Stream
;还可以配合async
和await
来使用,以同步的方式处理异步任务等等。
转载自:https://juejin.cn/post/6844904161675313166