Dart异步编程之使用Future.then和async/await消除回调地狱
假设有如下三个异步任务分别为登录、获取用户信息、和保存用户信息。我们的代码要实现的功能是登录成功后获取用户信息然后保存用户信息到本地。这三个任务是按顺序且有依赖关系的,获取用户信息任务依赖登录接口返回的用户id,保存用户信息任务依赖获取用户信息任务返回的结果。
注意每个方法后的async关键字表示该方法是个异步方法,返回的是一个Future
Future<String> login(String userName, String pwd) async {
return '88888888';
}
Future<String> getUserInfo(String id) async {
return '野猿新一是一只Android程序员';
}
Future saveUserInfo(String userInfo) async {
print(userInfo);
}
回调地狱
执行的任务流如下,在每一个任务的回调里执行下一个任务,如果相互依赖的任务很多,就会有很多的回调,这就是所谓的回调地狱callback hell,这样的代码可读性非常差,难以维护,一点都不优雅
void main() {
login('野猿新一', '1234556').then((id){
print('登录成功,用户id为${id}');
getUserInfo(id).then((userInfo){
print('获取用户信息成功,结果为${userInfo}');
saveUserInfo(userInfo).then((data){
print('保存用户信息成功');
});
});
});
}
输出结果如下
登录成功,用户id为88888888
获取用户信息成功,结果为野猿新一是一只Android程序员
野猿新一是一只Android程序员
保存用户信息成功
Future.then链式调用
上面定义的每一个方法都是异步的,都是返回一个Future,所以还有一种比较优雅的实现方法,就是用Future.then的链式调用,在上一个任务的回调里返回下一个任务的执行结果,也就是返回一个Future,而这个任务的回调可以通过Future.then链式调用的方式拼在上一个then的后面,这就避免了多层的回调,执行代码如下所示
void main() {
login('野猿新一', '1234556').then((id){
print('登录成功,用户id为${id}');
return getUserInfo(id);
}).then((userInfo){
print('获取用户信息成功,结果为${userInfo}');
return saveUserInfo(userInfo);
}).then((data){
print('保存用户信息成功');
});
}
执行结果如下,与上一种方法的执行结果一样,但是避免了多层的回调地狱,虽然每一个then里面还是有一个回调,但是只有一层,逻辑还是比较清晰的
登录成功,用户id为88888888
获取用户信息成功,结果为野猿新一是一只Android程序员
野猿新一是一只Android程序员
保存用户信息成功
async/await组合
Future.then的链式调用虽然已经很简洁了,但是还是有一层回调,使用async/await还可以使代码更加简洁易懂,看起来就像同步代码一样
async/await是借鉴了JavaScript中的,功能和用法完全一样,连这两个关键字都一样。利用async/await可以把异步的任务写成同步的样子,看起来极度舒适,简直爱不释手
实现代码如下所示,有以下几点注意事项:
- 在调用异步方法的前面多加了一个await关键字,表示这是异步返回,等该异步任务执行成功了才会执行下一行代码
- awsit异步调用只能在异步方法里面,否则会报"The await expression can only be used in an async function."错误,如下的task()方法后面有一个async关键字就是一个异步方法
- 异步的任务难免会发生异常,所以建议用try/catch代码块包裹起来,在catch里面处理异常
void main() {
task();
}
void task() async {
try {
String id = await login('野猿新一', '1234556');
print('登录成功,用户id为${id}');
String userInfo = await getUserInfo(id);
print('获取用户信息成功,结果为${userInfo}');
saveUserInfo(userInfo);
print('保存用户信息成功');
} catch (e) {
print(e);
}
}
执行结果如下
登录成功,用户id为88888888
获取用户信息成功,结果为野猿新一是一只Android程序员
野猿新一是一只Android程序员
保存用户信息成功