异步在编程中是一个重要的概念,很多高级语言都是单线程语言,它们在进行一些耗时操作时会造成线程阻塞,例如 Dart 和 JavaScript 都是单线程的,并且都提供了一些相似的特性来支持异步编程,这对于一个学习 Dart 的 JS 开发者无疑是极其舒适的。
Future 对象
Future
和 ECMAScript 6 的 Promise
几乎是一个特性,它们都是异步编程的解决方案,Future
是基于观察者模式的,它有 3 种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)。
我们可以使用构造函数来实例化一个 Future
对象,例如:
void main() {
final request = Future<String>(() => 'request success');
print(request);// Instance of 'Future<String>'
}
Future
构造函数接受一个函数作为参数,泛型参数决定了返回值的类型,在上面的例子中,Future
返回值被规定为 String
。
Future
实例生成后,可以用 then
方法指定成功状态的回调函数,例如:
void main() {
final request = Future<String>(() => 'request success');
print(request);// Instance of 'Future<String>'
request.then((e) => print(e));// output: request success
}
then
方法还可以接受一个可选命名参数,参数的名称是 onError
,即失败状态的回调函数,例如:
void main() {
final request = Future<String>(() {
throw new FormatException('Expected at least 1 section');
});
final then = request.then((e) => print('success'), onError: (e) => print(e));
print(then);
/**
* output:
* Instance of 'Future<void>'
* FormatException: Expected at least 1 section
*/
}
上面的代码中,Future 实例的函数中抛出了异常,被 onError
回调函数捕获到,并且可以看出 then
方法返回的还是一个 Future
对象,所以其实我们还可以利用 Future
对象的 cathError
进行链式调用从而捕获异常,用法如下:
void main() {
final request = Future<String>(() {
throw new FormatException('Expected at least 1 section');
});
request.then((e) => print('success'))
.catchError((e) => print(e));// output: FormatException: Expected at least 1 section
}
Dart 中也内置了很多方法会返回 Future
对象,例如,File
对象的 readAsString
方法,此方法是异步的,它用于读取文件,调用此方法将返回一个 Future
对象。
async 函数与 await 表达式
使用方法
使用 async
关键字可以声明一个异步方法,并且该方法会返回一个 Future
,例如:
Future<String> getVersion() async {
return 'v1.0';
}
checkVersion() async => true;
void main() {
print(getVersion());// output: Instance of 'Future<String>'
print(checkVersion());// output: Instance of 'Future<dynamic>'
}
await
表达式必须放入 async
函数体内才能使用,await
表达式会对代码造成阻塞,直至异步操作完成,例如:
void main() async {
await Future(() => print('request success'));
print('test');
/**
* output:
* request success
* test
*/
}
优点
await
表达式能够使异步操作变得更加方便,之前我们使用 Future
对象进行连续的异步操作时,类似如下代码:
void main() {
Future<String>(() => 'request1')
.then((res) {
print(res);
return Future<String>(() => 'request2');
})
.then((res) {
print(res);
return Future<String>(() => 'request3');
})
.then(print);
/**
* output:
* request1
* request2
* request3
*/
}
上面的代码中,每个异步操作都需要等待上个异步操作的完成才可进行,异步回调 then
方法是个链式操作,如果我们使用 await
表达式,可以让这些连续的异步操作将变得更加可读,看来起来就像是同步操作,并且拥有相同的效果,例如:
void main() async {
final res1 = await Future<String>(() => 'request1');
print(res1);// output: request1
final res2 = await Future<String>(() => 'request2');
print(res2);// output: request2
final res3 = await Future<String>(() => 'request3');
print(res3);// output: request3
}
异常处理
因为 await
表达式后面是一个 Future
对象,所以我们可以使用 catchError
来捕获 Future
的异常:
void main() async {
final res1 = await Future<String>(() => throw 'is error').catchError(print);
print(res1);
/**
* output:
* is error
* null
*/
}
或者直接使用 try
、catch
和 finally
来处理异常:
void main() async {
try {
final res = await Future<String>(() => throw 'is error');
} catch(e) {
print(e);// output: is error
}
}