Dart中的Future使用

本文首发于个人博客

前言

关于异步,相信很多开发者都是经常用到的。不过,不同的语言有不同的处理方式

一般耗时操作的处理

针对如何处理耗时的操作,不同的语言有不同的处理方式。

  • 处理方式一: 多线程,比如JavaC++Objective C我们普遍的做法是开启一个新的线程(Thread),在新的线程中完成这些异步的操作,再通过线程间通信的方式,将拿到的数据传递给主线程。

  • 处理方式二: 单线程+事件循环,比如JavaScriptDart都是基于单线程加事件循环来完成耗时操作的处理。

阻塞式调用和非阻塞式调用

  • 阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态。

  • 阻塞式调用: 调用结果返回之前,当前线程会被挂起,调用线程只有在得到调用结果之后才会继续执行。

  • 非阻塞式调用: 调用执行之后,当前线程不会停止执行,只需要过一段时间来检查一下有没有结果返回即可。

例如你平时做饭的时候,把米放在电饭煲里面煮,这时候你可以在旁边坐着等着米饭做好,然后去做菜,这就是阻塞式调用。当然了。你也可以煮饭时候,准备菜,啤酒鸭,麻辣小龙虾。。这就是非阻塞式调用。

Dart事件循环

什么是事件循环

单线程模型中主要就是在维护着一个事件循环(Event Loop)。

事件循环是什么呢?

  • 事实上事件循环并不复杂,它就是将需要处理的一系列事件(包括点击事件、IO事件、网络事件)放在一个事件队列(Event Queue)中。

  • 不断的从事件队列(Event Queue)中取出事件,并执行其对应需要执行的代码块,直到事件队列清空位置。

  • 当我们有一些事件时,比如点击事件、IO事件、网络事件时,它们就会被加入到eventLoop中,当发现事件队列不为空时发现,就会取出事件,并且执行。

具体使用

例子

我们先看一个例子:如下代码中,用sleep代替网络请求的耗时操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import 'dart:io';

main(List<String> args) {

print("start");
String res = getData();
print(res);
print("end");

}

String getData(){
sleep(Duration(seconds: 2));
return "hello world";
}

输出结果为:

start

2秒钟之后输出

hello world

end

很显然阻塞了后面的代码执行

使用Future

Future 表示一件“将来”会发生的事情,将来可以从Future中取到一个值。
有了Future之后,通过.then的回调去获取请求到的结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import 'dart:io';

main(List<String> args) {

print("start");
var res = getData();
res.then((value) {
print(value);
});
print("end");

}

Future<String> getData(){

return Future<String>(() {
sleep(Duration(seconds: 2));
return "hello world";
});

}

catchError

如果有异常的话,我们可以用catchError来捕获

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import 'dart:io';

main(List<String> args) {

print("start");
var res = getData();
res.then((value) {
print(value);
}).catchError((error){// 捕获出现异常时的情况
print(error);

});
print("end");

}

Future<String> getData(){

return Future<String>(() {
sleep(Duration(seconds: 2));
// return "hello world";
throw Exception("请求异常");

});

}

输出为

start

end

Exception: 请求异常

Future的链式调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import 'dart:io';

main(List<String> args) {

print("start");
var res = getData();
res.then((value) {
print(value);
return "第一次调用完成";
}).then((value2){

print(value2);
return "第二次调用完成";
}).then((value3){

print(value3);
}).catchError((error){
print(error);

});
print("end");

}

Future<String> getData(){

return Future<String>(() {
sleep(Duration(seconds: 2));
return "hello world";
// throw Exception("请求异常");
});

}

输出

start

end

hello world

第一次调用完成

第二次调用完成

直接获取一个完成的Future,该Future会直接调用then的回调函数

1
2
3
4
5
6
7
8
9
10
11
12
import 'dart:io';

main(List<String> args) {

print("start");

Future.value("测试").then((value){
print(value);
});
print("end");

}

输出为:

start

end

测试

我们可以看到,测试是最后才打印,这是因为Future中的then会作为新的任务会加入到事件队列中(Event Queue),加入之后你需要排队执行。

总结

  • 创建一个Future(可能是我们创建的,也可能是调用内部API或者第三方API获取到的一个Future,总之你需要获取到一个Future实例,Future通常会对一些异步的操作进行封装);
  • 通过.then(成功回调函数)的方式来监听Future内部执行完成时获取到的结果;
  • 通过.catchError(失败或异常回调函数)的方式来监听Future内部执行失败或者出现异常时的错误信息;
  • 可以链式调用
  • 直接获取一个完成的Future,该Future会直接调用then的回调函数

QQ交流群:592831498

个人博客https://ityongzhen.github.io