Dart中异步编程之Future详解同步VS异步 同步方法或操作是指一个任务在执行时,必须等待另一个任务完成后才能继续执
同步VS异步
同步方法或操作是指一个任务在执行时,必须等待另一个任务完成后才能继续执行。换句话说:所有任务按照顺序一个接一个地执行,调用者在等待操作完成之前会被阻塞,无法继续执行其他任务。异步方法或操作是指一个任务在执行时,不需要等待前一个任务完成就可以继续执行其他任务。异步操作通常通过回调、Promise、Future等机制来处理结果。调用者不需要等待操作完成,可以继续执行其他任务,当操作完成时,会通过回调或其他机制通知调用者。换句话说:所有任务可以并行执行多个任务(不同任务在不同的CPU核上执行,或者单核分时段执行不同任务)。当执行耗时的任务的时候,我们就需要使用异步任务,防止耗时长导致掉帧甚至出现卡顿以及ANR,比如:文件读写,网络请求,数据库读写,以及CPU密集型的任务的处理。
Future简介
Future是Dart中一个非常重要的异步编程的概念,它是指未来某个时间需要完成的操作或者任务,它可以让我们在不阻塞当前任务的情况下执行一个任务,并在任务完成后获得相应的结果。
Future状态转换
代码示例如下所示:
void main() async{
age().then((value) {
print(value);
}, onError: (e) {
print("catchError $e");
}
);
}
Future<int> age() async {
throw Exception('发生错误');
// return 35;
}
Future有三种状态:初始状态(Uncompleted),数据完成状态(Completed with data),异常完成状态(Completed with error)。Future创建的时候是初始状态(当调用age方法的时候),当调用then回调函数中有两个参数,一个onValue方法回调(代表有返回值),一个onError方法回调(代表返回异常)。Future执行结果要么是执行onValue,要么是执行onError。除了用onError回调,还可以使用catchError方法监听异常错误信息。上述代码,当Future完成时会打印错误信息。
多个Future的顺序执行
在Flutter中处理复杂的异步代码时,尤其是嵌套多个Future.then()调用时,代码很快就会变得难以阅读和维护。这通常被称为“回调地狱”或“then 地狱”。示例代码如下所示:
void fetchData() {
fetchDataFromApi()
.then((data) {
processData(data).then((processedData) {
saveData(processedData).then((result) {
print('Data saved successfully!');
}).catchError((error) {
print('Failed to save data: $error');
});
}).catchError((error) {
print('Failed to process data: $error');
});
})
.catchError((error) {
print('Failed to fetch data: $error');
});
}
then方法的嵌套回调让代码的可读性和可维护性大大降低。Dart的async和await关键字帮助您编写看起来更像同步代码的异步代码,使其更易于阅读和维护。
Future<void> fetchData() async {
try {
final data = await fetchDataFromApi();
final processedData = await processData(data);
await saveData(processedData);
print('Data saved successfully!');
} catch (error) {
print('An error occurred: $error');
}
}
更改之后的例子有三方面的好处。
- 可读性:代码从上到下阅读,更容易理解。
- 错误处理:您可以使用 try-catch 块在一个地方处理错误,从而简化逻辑。
- 可维护性:在添加、删除或修改步骤中的异步请求非常简单。 这个编程范式让异步的嵌套代码复杂度直接从N变为了1,这样就让代码出错的概率大大减小。
多个Future都执行完毕
有时候业务需求需要多个异步任务都执行完毕,才能执行某些操作,这时候就需要用Future.wait()方法来实现。wait的适用场景:比如请求数据库中A表的数据,请求数据库中B表的数据,两个数据集再汇合成需要的数据,这时候wait方法。
void main() async{
print(fetchData());
print(await Future.wait([fetchData(), fetchData2()]));
print("All done!");
}
Future<String> fetchData() async {
await Future.delayed(const Duration(seconds: 5));
return "fetchData";
}
Future<String> fetchData2() async {
await Future.delayed(const Duration(seconds: 3));
return "fetchData2";
}
fetchData和fetchData2这两个异步操作都返回结果之后,Future.wait才会把结果按照List集合返回,wait的耗时是两个异步操作的最长耗时也就是5秒。如果Future没有调用then方法,或者前面没有await,会返回Instance of '_Future'。Future.delayed(const Duration(seconds: 3))是为了让方法变成耗时的方法,这样的代码多用于测试代码,模拟延时。代码的输出如下:
Instance of '_Future<String>'
[fetchData, fetchData2]
All done!
多个Future同时执行,返回最快的结果
有时候需要多个异步任务中最快任务,执行完毕就返回结果,然后可以执行其他业务操作,这时候就需要用Future.any()方法来实现。any的适用场景:比如可以通过网络请求数据,也可以通过本机器的缓存来请求数据,两个同时请求用any返回最快的数据即可。
void main() async {
print("begin");
print(await Future.any([fetchData(), fetchData2()]));
print("fastest done!");
}
Future<String> fetchData() async {
await Future.delayed(const Duration(seconds: 5));
return "fetchData";
}
Future<String> fetchData2() async {
await Future.delayed(const Duration(seconds: 3));
return "fetchData2";
}
Future在Flutter UI业务中的使用
在创建Widget时,initState()方法会被调用一次,这使其成为运行只应调用一次的Future的理想场所。可以根据FutureBuilder中状态不同,进行不同的UI展示。示例代码如下所示:
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
late Future<String> _futureData;
@override
void initState() {
super.initState();
// Future is initialized here and will run only once
_futureData = fetchData();
}
Future<String> fetchData() async {
// Simulate a network call or other async task
await Future.delayed(Duration(seconds: 2));
return "Data Loaded";
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<String>(
future: _futureData,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
} else if (snapshot.hasError) {
return Text("Error: ${snapshot.error}");
} else {
return Text("Data: ${snapshot.data}");
}
},
),
);
}
}
总结
异步编程是软件开发过程中为了提高性能不得不考虑的一个方面。使用Future,可以将耗时任务放在后台执行,从而防止主线程卡顿。Future是一个非常底层的类,它提供了很多方法,比如等待所有Future执行完成的wait()方法,返回执行最快的Future的any()方法,这些方法的组合就能完成基本的业务需求开发。祝您编码愉快!
转载自:https://juejin.cn/post/7403547816915795994