编程中的代码执行,通常分为同步与异步两种。简单说,同步就是按照代码的编写顺序,从上到下依次执行,这也是最简单的我们最常接触的一种形式。但是同步代码的缺点也显而易见,如果其中某一行或几行代码非常耗时,那么就会阻塞,使得后面的代码不能被立刻执行。
异步的出现正是为了解决这种问题,它可以使某部分耗时代码不在当前这条执行线路上立刻执行,那究竟怎么执行呢?最常见的一种方案是使用多线程,也就相当于开辟另一条执行线,然后让耗时代码在另一条执行线上运行,这样两条执行线并列,耗时代码自然也就不能阻塞主执行线上的代码了。
多线程虽然好用,但是在大量并发时,仍然存在两个较大的缺陷,一个是开辟线程比较耗费资源,线程开多了机器吃不消,另一个则是线程的锁问题,多个线程操作共享内存时需要加锁,复杂情况下的锁竞争不仅会降低性能,还可能造成死锁。因此又出现了基于事件的异步模型
。简单说就是在某个单线程中存在一个事件循环和一个事件队列,事件循环不断的从事件队列中取出事件来执行,这里的事件就好比是一段代码,每当遇到耗时的事件时,事件循环不会停下来等待结果,它会跳过耗时事件,继续执行其后的事件。当不耗时的事件都完成了,再来查看耗时事件的结果。因此,耗时事件不会阻塞整个事件循环,这让它后面的事件也会有机会得到执行。
我们很容易发现,这种基于事件的异步模型,只适合I/O密集型的耗时操作,因为I/O耗时操作,往往是把时间浪费在等待对方传送数据或者返回结果,因此这种异步模型往往用于网络服务器并发。如果是计算密集型的操作,则应当尽可能利用处理器的多核,实现并行计算。
于处理异步操作,异步处理成功了就执行成功的操作,异步处理失败就捕获错误或者停止后续操作,一个Future只会对应一个结果,要么成功,要么失败。
Future的所有API的返回值仍然是一个Future对象,所以可以很方便的进行链式调用。
模拟延时操作
then中接收异步结果并打印结果
Future.delayed(new Duration(seconds: 2),(){
return "hi world!";
}).then((data){
print(data);
});
如果异步任务发生错误,可以在catchError中捕获错误
Future.delayed(new Duration(seconds: 2),(){
//return "hi world!";
throw AssertionError("Error");
}).then((data){
//执行成功会走到这里
print("success");
}).catchError((e){
//执行失败会走到这里
print(e);
});
then 接收结果,catchError铺货异常,但并非只有catchError回调才能铺货错误,then方法还有一个可选参数onError,也可以铺货异常
无论异步任务执行成功或失败都需要做一些事时,
Future.delayed(new Duration(seconds: 2),(){
//return "hi world!";
throw AssertionError("Error");
}).then((data){
//执行成功会走到这里
print(data);
}).catchError((e){
//执行失败会走到这里
print(e);
}).whenComplete((){
//无论成功或失败都会走到这里
});
如果需要等待多个异步任务都执行结束后做某些操作,可以使用Future.wait,它接受一个Future数组参数,
Future.wait([
// 2秒后返回结果
Future.delayed(new Duration(seconds: 2), () {
return "hello";
}),
// 4秒后返回结果
Future.delayed(new Duration(seconds: 4), () {
return " world";
})
]).then((results){
print(results[0]+results[1]);
}).catchError((e){
print(e);
});
Future.delay延迟两秒后执行第二个参数里面的内容,返回一个Future对象,执行then后面的内容,then里面方法的参数为delayed第二个参数方法返回的内容,也就是"Hello World!"
Future.delayed(new Duration(seconds: 2),(){
return "Hello World!";
}).then((data){
print(data);
});
由于then方法返回还是一个Future,所以可以用链式调用一直拼接then执行相关代码
Future.delayed(new Duration(seconds: 2),(){
return 'Hello';
}).then((data){
return data + ' World';
}).then((data){
return data + ' and';
}).then((data){
return data + ' 野猿新一!';
}).then((data){
print(data);
}).catchError((e){
print(e);
});
运行结果如下
Hello World and 野猿新一!
其实不止catchError方法可以捕获异常,在then方法中还提供了一个可选参数onError,当发生异常的时候,会走到onError参数所传入的方法
Future.delayed(new Duration(seconds: 2), () {
// 2秒后抛出一个异常
throw AssertionError("Error");
}).then((data) {
// 正常执行后回调该方法
print("success");
}, onError: (e) {
// 发生异常后回调该方法
print(e);
});
我们再来看下,若同时设置then方法的onError参数和调用catchError方法,当发生异常时程序会走到哪
Future.delayed(new Duration(seconds: 2), () {
throw AssertionError("Error");
}).then((data) {
print("success");
}, onError: (e) {
print('onError ' + e.toString());
}).catchError((e){
print('catchError ' + e.toString());
});
输出结果如下
onError Assertion failed
可以看到当同时设置onError和catchError的时候,当发生异常时,程序只会走onError,而不会走到catchError
当然实际写代码的也无需两个都写,这里我比较推荐写catchError,链式调用这样的代码层次比较清晰
Futere
future是一个Futere的一个泛型对象,表示一个异步操作的结果是T类型。如果这个结果不是可以直接使用,可以使用Future。 当调用返回future的函数时,会发生2件事。
函数队列开始执行,结束的时候返回一个Future对象.
当这个操作结束的时候,这个Future对象会返回一个值或者error.
当需要依赖Future去编码时,可以有两个选择
使用asyn和await
使用Future的API
//视频模块的接口
Future<Vediomessage> getVedioMessage() async {
int courseId = Store.read<CourseItemInfo>(context).info.courseId;
Vediomessage data;
var response = await FHttp.get('/S9?cou_id=${courseId}');
var model = Vediomessage.fromJson(response);
if (model.code == "200") {
print(200);
data = model;
}
return data;
}
import 'dart:convert';
class Vediomessage {
String msg;
String code;
List<String> data;
Vediomessage({this.msg, this.code, this.data});
Vediomessage.fromMap(Map<String, dynamic> json) {
msg = json['msg'];
code = json['code'];
data = json['data'].cast<String>();
}
Map<String, dynamic> toMap() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['msg'] = this.msg;
data['code'] = this.code;
data['data'] = this.data;
return data;
}
String toJson() =>
json.encode(toMap()); //将一个json格式的string 转化成一个Map类型的Map
static Vediomessage fromJson(source) =>
Vediomessage.fromMap(new Map<String, dynamic>.from(source));
}