最近用Flutter写了个简单的“爬虫”的东西,就是要不断地请求某些接口,而后读取数据就能够了。以前只是会简单地await和async来试用Future,后面发现只会简单的这种方式不行,因而去学习了Future的其余用法,仍是有点收获的,把Future的用法都学了一下,这里作个笔记。java
哈哈,别问我为何没用python去搞。。。恰好用的电脑没装环境而且python的API没那么熟,就有了个想法,反正都能实现,就用Flutter玩一下吧,可能后面还能够搞个可视化界面玩玩。python
推荐文章:Dart与消息循环机制bash
这里简单说下Dart的事件循环,更加具体的话,能够看推荐的这篇文章。网络
相似Android的Handler/Looper机制,Dart也有相应的消息循环机制。dom
在写程序的过程当中,确定会有一部分比较耗时代码是须要异步执行的。好比网络操做,咱们须要异步去请求数据,而且还须要处理请求成功和请求失败的两种状况。异步
在Flutter中,使用Future来执行耗时操做,表示在将来会返回某个值,并可使用then()方法和catchError()来注册callback来监听Future的处理结果。async
以下面所示,源码中对Future的解释,以及简单的一个例子。then()方法和catchError()返回的其实也是一个Future类型对象,因此能够写成链式调用的形式。函数
An object representing a delayed computation.
Future<int> future = getFuture();
future.then((value) => handleValue(value))
.catchError((error) => handleError(error));
复制代码
一个Future对象会有如下两种状态oop
注意:首先Future是个泛型类,能够指定类型。若是没有指定相应类型的话,默认返回是Future类型的。学习
Future createFuture()async{
return 1;
}
var future=createFuture();
print(future.runtimeType);。//输出结果为Future<dynamic>
复制代码
Future经常使用的建立有如下几种方式。
Future的构造方法,建立一个基本的Future
var future = Future(() {
print("哈哈哈");
});
print("111");
//111
//哈哈哈
复制代码
你会发现,结果是打印“哈哈哈”字符串是在后面才输出的,缘由是默认future是异步执行的。若是改为下面的代码,在future前面加上await关键字,则会先打印“哈哈哈”字符串。await会等待直到future执行结束后,才继续执行后面的代码。
这个跟上面的事件循环是有关系的,转发大佬的解释:main方法中的普通代码都是同步执行的,因此确定是main打印先所有打印出来,等main方法结束后会开始检查microtask中是否有任务,如有则执行,执行完继续检查microtask,直到microtask列队为空。因此接着打印的应该是microtask的打印。最后会去执行event队列。
var future = await Future(() {
print("哈哈哈");
});
print("111");
//哈哈哈
//111
复制代码
建立一个返回指定value值的Future
var future=Future.value(1);
print(future);
//Instance of 'Future<int>'
复制代码
建立一个延迟执行的future。 例以下面的例子,利用Future延迟两秒后能够打印出字符串。
var futureDelayed = Future.delayed(Duration(seconds: 2), () {
print("Future.delayed");
return 2;
});
复制代码
根据某个集合,建立一系列的Future,而且会按顺序执行这些Future。 好比下面的例子,根据{1,2,3}建立3个延迟对应秒数的Future。执行结果为1秒后打印1,再过2秒打印2,再过3秒打印3,总时间为6秒。
Future.forEach({1,2,3}, (num){
return Future.delayed(Duration(seconds: num),(){print(num);});
});
复制代码
用来等待多个future完成,并收集它们的结果。那这样的结果就有两种状况了:
好比下面的例子,也是建立3个延迟对应秒数的Future。结果是总时间过了3秒后,才输出[1, 2, 3]的结果。能够与上面的例子对比一下,一个是顺序执行多个Future,一个是异步执行多个Future。
var future1 = new Future.delayed(new Duration(seconds: 1), () => 1);
var future2 =
new Future.delayed(new Duration(seconds: 2), () => 2);
var future3 = new Future.delayed(new Duration(seconds: 3), () => 3);
Future.wait({future1,future2,future3}).then(print).catchError(print);
//运行结果: [1, 2, 3]
复制代码
将future2和future3改成有error抛出,则future.wait的结果是这个future2的error值。
var future1 = new Future.delayed(new Duration(seconds: 1), () => 1);
var future2 =new Future.delayed(new Duration(seconds: 2), () => throw “throw error2"); var future3 = new Future.delayed(new Duration(seconds: 3), () => throw “throw error3");
Future.wait({future1,future2,future3}).then(print).catchError(print);
//运行结果: throw error2
复制代码
返回的是第一个执行完成的future的结果,不会管这个结果是正确的仍是error的。(感受这个的使用场景没几个的样子,想不到有什么场景要用到这个。)
例以下面的例子,使用Future.delayed()延迟建立了三个不一样的Future,第一个完成返回的是延迟1秒的那个future的结果。
Future
.any([1, 2, 5].map(
(delay) => new Future.delayed(new Duration(seconds: delay), () => delay)))
.then(print)
.catchError(print);
复制代码
相似java中doWhile的用法,重复性地执行某一个动做,直到返回false或者Future,退出循环。
使用场景:适用于一些须要递归操做的场景。
好比我就是要爬某个平台的商品分类的数据,那每一个分类下面又有相应的子分类。那我就得拿对应父级分类的catId去请求接口,拿到这个分类下面的全部子分类。一样的,对全部的子分类,也要进行同样的操做,递归直到这个分类是一个叶子节点,也就是该节点下面没有子分类(有个leaf为true的字段)。哈哈,代码我就没贴了,其实也就是一个递归操做,直到知足条件退出future。
例以下面的例子,生成一个随机数进行等待,直到十秒以后,操做结束。
void futureDoWhile(){
var random = new Random();
var totalDelay = 0;
Future
.doWhile(() {
if (totalDelay > 10) {
print('total delay: $totalDelay seconds');
return false;
}
var delay = random.nextInt(5) + 1;
totalDelay += delay;
return new Future.delayed(new Duration(seconds: delay), () {
print('waited $delay seconds');
return true;
});
})
.then(print)
.catchError(print);
}
//输出结果:
I/flutter (11113): waited 5 seconds
I/flutter (11113): waited 1 seconds
I/flutter (11113): waited 3 seconds
I/flutter (11113): waited 2 seconds
I/flutter (11113): total delay: 12 seconds
I/flutter (11113): null
复制代码
建立一个在microtask队列运行的future。
在上面讲过,microtask队列的优先级是比event队列高的,而通常future是在event队列执行的,因此Future.microtask建立的future会优先于其余future进行执行。
例以下面的代码,
Future((){
print("Future event 1");
});
Future((){
print("Future event 2");
});
Future.microtask((){
print("microtask event");
});
//输出结果
//microtask event
//Future event 1
//Future event 2
复制代码
Flutter提供了下面三个方法,让咱们来注册回调,来监听处理Future的结果。
Future<R> then<R>(FutureOr<R> onValue(T value), {Function onError});
Future<T> catchError(Function onError, {bool test(Object error)});
Future<T> whenComplete(FutureOr action());
复制代码
用于在Future完成的时候添加回调。
注意:then()的返回值也是一个Future对象。因此咱们可使用链式的方法去使用Future,将前一个Future的输出结果做为后一个Future的输入,能够写成链式调用。
例以下面的代码,将前面的future结果做为后面Future的输入。
Future.value(1).then((value) {
return Future.value(value + 2);
}).then((value) {
return Future.value(value + 3);
}).then(print);
//打印结果为6
复制代码
注册一个回调,来处理有error的Future
new Future.error('boom!').catchError(print);
复制代码
与then方法中的onError的区别:then方法里面也有个onError的参数,也能够用来处理错误的Future。
二者的区别,onError只能处理当前的错误Future,而不能处理其余有错误的Future。catchError能够捕获到Future链中抛出的全部错误。
因此一般的作法是使用catchError来捕捉Future中的全部错误,不建议使用then方法中的onError方法。否则每一个future的then方法都要加上onError回调的话,就比较麻烦了,并且代码看起来也是有点乱。
下面是两个捕捉错误的例子。
在那个抛出错误的future的then方法里添加onError回调的话,onError会优先被调用
Future.value(1).then((value) {
return Future.value(value + 2);
}).then((value) {
throw "error";
}).then(print, onError: (e) {
print("onError find a error");
}).catchError((e) {
print("catchError find a error");
});
//输出结果为onError find a error
复制代码
使用catchError来监听链式调用Future里面抛出来的错误。
Future.value(1).then((value) {
throw "error";
}).then((value) {
return Future.value(3);
}).then(print).then((value) {
}).catchError((e) {
print("catchError find a error");
});
//输出结果为catchError find a error" 复制代码
相似Java中的finally,Future.whenComplete老是在Future完成后调用,无论Future的结果是正确的仍是错误的。
注意:Future.whenComplete的返回值也是一个Future对象。
Future.delayed(Duration(seconds: 3),(){
print("哈哈");
}).whenComplete((){
print("complete");
});
//哈哈
//complete
复制代码
哈哈,上面说了一些Future的用法,其实有些可能并不会很经常使用,知道有这个用法就好了。
有个问题,若是有多个Future连接在一块儿的话,靠,代码可能会变得难以阅读。
因此,可使用async和await来使用Future,使代码看起来像是同步的代码,但实际上它们仍是异步执行的。
最经常使用的仍是async和await来使用Future。
注意:await只能在async函数出现。 async函数,返回值是一个Future对象,若是没有返回Future对象,会自动将返回值包装成Future对象。 捕捉错误,通常是使用try/catch机制来作异常处理。 await 一个future,能够拿到Future的结果,直到拿到结果,才执行下一步的代码。
好比下面的例子,建立一个延迟3秒并返回结果为1的Future,使用await来获取到future的值。
void main() async {
var future = new Future.delayed(new Duration(seconds: 3), () {
return 1;
});
var result = await future;
print(result + 1);
}
//输出为2
复制代码
下面的代码结构,跟java同样的写法,使用try-catch-finally来捕捉错误。
try {
var result1 = await Future.value(1);
print(result1);//输出1
var result2 = await Future.value(2);
print(result2);//输出2
} catch (e) {
//捕捉错误
} finally {
}
复制代码
多学习多整理。靠,又懒了好长一段时间了。。。