Appearance
Future
Future是Dart中一个很重要的概念,Future是一个异步操作,它代表一个异步任务的执行结果。Future 不是多线程的。它是一种异步编程的机制,用于处理非阻塞式的计算任务,并在任务完成后返回结果。Future使用单线程执行任务,并通过事件循环(event loop)模型来实现异步操作。
Flutter是单线程UI框架,所以不要把Future线程混淆。Future是异步操作,它代表一个异步任务的执行结果。在实际的Flutter应用中,可以时逻辑从某种程度上避免和UI渲染同时操作,比如渲染时同时出发请求,但是过多的异步操作同样会导致性能问题使得整个UI线程阻塞,所以要同时避免过多的异步操作。
虽然 Future 不是多线程的,但可以使用 async 和 await 关键字来编写更简洁和易读的异步代码。通过使用 async 关键字标记一个函数为异步函数,并在需要等待异步任务结果的地方使用 await 关键字,可以让代码看起来像是同步执行的。
多线程与Future
前面我们说到过,Future只是线程内异步操作的实现,并不属于多线程概念。而且由于Dart是单线程的,因此无法利用多核处理器来并行执行多个任务。然而,Dart 提供了 Isolate(隔离区)机制,它是 Dart 中实现并发的一种方式。通过使用 Isolate,可以在多个隔离区中并行执行多个任务,从而提高应用程序的性能。Isolate 之间通过消息传递进行通信,以确保线程安全。值得注意注意Dart多线程,Isolate之间并不共享内存。在后续的文章内,我们会提到多线程概念。
Future与Promise的差异
Future 和 Promise 是两种不同编程语言中用于处理异步操作的机制。理解起来,Future与Promise十分类似。但返回结果的方式不同:Future 在执行异步操作后,会返回一个 Future 对象,表示将来会返回任务的结果。在代码中,我们可以使用 await 关键字等待 Future 的结果。而 Promise 在执行异步操作后,会返回一个 Promise 对象,我们可以使用 .then() 方法来处理异步操作结束后的结果。
Future 是 Dart 中使用的异步编程的机制,主要依赖于 async/await 关键字来编写异步代码。这种方式使代码看起来像是同步执行的。而 Promise 是在 JavaScript 中使用的异步编程机制,主要依赖于 .then() 和 .catch() 方法来处理异步操作。
Future的使用方法
最常见的,就是申明一个异步函数。
dart
Future<String> fetchData() async {
return Future.value('Hello, World!');
}
final a = await fetchData(); /// Hello, World!
final a = fetchData(); /// Future<String>
但是我们上面提到过Future 在执行异步操作后,会返回一个 Future 对象,利用这个特性,我们还可以这样使用Future
dart
Future((){
print('Hello, World!');
});
这样,直接异步执行一个函数,这样做的场景在组件中,其实非常实用,因为很多时候StatefulWidget的initState方法中,调用异步方法,可以避免组件初始化时,造成掉帧。
dart
@override
void initState() {
super.initState();
Future(()async{
await doSomething();
})
}
通过使用 Future.wait() 方法,可以同时等待多个异步任务的完成,并进行并行处理。
dart
void main() async {
Future<String> task1 = fetchData1();
Future<int> task2 = fetchData2();
List<dynamic> results = await Future.wait([task1, task2]);
String data = results[0];
int number = results[1];
print('Data fetched successfully: $data');
print('Number fetched successfully: $number');
}
错误处理
在 Future 中,可以使用 try/catch 块来捕获异步操作中的错误。
dart
void main() async {
try {
String data = await fetchData();
print('Data fetched successfully: $data');
} catch (error) {
print('Error occurred: $error');
}
}
语法误区
首先如果一个异步函数中,没有定义返回数据类型为Future<T>
那么返回值不是一个Future,则不能使用await,比如下面的代码中是有差异的。
dart
Future<String> fetchData() async {
return Future.value('Hello, World!');
}
void fetchDataB() async {
return Future.value('Hello, World!');
}
final a = await fetchData(); /// Hello, World!
final a = await fetchDataB(); /// 由于返回值不是Future,所以await会报错
上述代码中,你可能注意到我们返回时,使用了 Future.value('Hello, World!')
,在dartpad.dev中,你可以运行一下一下的代码试试结果:
dart
Future<String> fetchData() async {
return 'Hello, World!';
}
提前终止
在Dart中,Future 实例是无法主动终止的,因为它代表的是一个异步操作的结果。一旦 Future 开始执行,它会一直执行直到完成。然而,你可以通过一些技巧来实现在某些条件下“终止”一个 Future 的效果。
使用 Completer:Completer 是一个用于手动完成一个 Future 的类。你可以创建一个 Completer 实例,并在某些条件下调用 complete 方法来完成 Future 对象。
dart
import 'dart:async';
void main() {
Completer<void> completer = Completer<void>();
Future<void> myFuture = completer.future;
// 在某个条件下,调用 complete 方法来完成 Future
if (condition) {
completer.complete();
} else {
completer.completeError('Terminated');
}
myFuture.then((value) {
print('Future completed successfully');
}).catchError((error) {
print('Future terminated with error: $error');
});
}
通过控制 Completer
的完成或错误完成,可以模拟 “终止” 一个 Future
。使用内部标志:在 Future
执行过程中,可以使用一个内部的标志来控制是否继续执行。在每个异步操作之前,检查这个标志并决定是否继续执行。
dart
bool shouldContinue = true;
Future<void> myFuture() async {
while (shouldContinue) {
// 执行异步操作
await doSomethingAsync();
}
}
通过修改 shouldContinue 标志,可以控制 Future 是否继续执行,并在必要时“终止” 它。 这些方法只能在某个条件下模拟“终止”一个 Future,而不是真正地终止它。在实际情况中,Future 执行过程中的异步操作可能无法立即中断,所以上述方法只是在满足某些条件时提前结束 Future 的执行。不要因为这个示例,就按照这样的方法去写一个复杂的终止网络请求的逻辑。实际上请求类一般都提供了CancelToken,可以取消请求。这个示例,目的是为了让我们掌握如何在Future执行中,减少不必要的运算。