目录导航
Dart
是单线程执行模式(
single-threaded execution model
),
Isoalte
是对线程的上层封装,代表一个执行环境。不同执行环境(Isolate)之间内存不共享。 Dart的异步操作不一定在另一个线程(
Isolate
)中执行,而且通常是在同一个线程(
Isolate
)中执行。
事件循环与事件队列
每个Isoalte
都包含一个事件循环器(event loop
)和与之关联的两个队列——事件队列(event queue
)和微任务队列(microtask queue
)。当Main Isolate
的Main
方法执行完毕后,event loop
开始依次从与之关联的队列里取出事件执行。这与Android中的Handler
机制非常类似,但与之不同的是,Isolate除了event queue外,还有一个microtask queue,microtask queue的优先级高于event queue
,event loop
总是优先处理microtask queue
里的事件。流程图如下:
如上图所示,event loop会首先判断microtask queue
是否为空。若不为空,依次取出mcrotask queue
中的事件处理,直至它为空;若为空,则尝试从event queue
中取出一个事件处理,处理完毕会再次判断microtask queue
的情况,不为空的话则优先处理microtask queue
,直至它为空。也就是说,每执行完event queue
中的一个事件,event loop
都要判断下microtask queue
的情况以优先处理它。microtask queue
的存在,使得事件处理可以存在“插队行为”。
future vs await
future
执行异步代码
我们通过Future
对象执行异步代码,Future
提供了多种方式传入异步代码,构造器传入是其中一种:
Future asyncMethod() {
return Future(() {
//异步代码
});
}
异步方法会同步返回Future
对象作为异步代码的执行结果的代言人,供调用方使用。
- 没有结果值的异步代码
Future printHelloWorld() {
return Future(() => print("Hello,world!"));
}
- 有结果值的异步代码
Future<int> provideOneInt() {
return Future(() => 1);
}
处理异步代码的结果
then
then
方法需传入一个结果处理函数,当异步代码执行完毕后,会执行该函数处理结果。结果处理函数携带一个形参,代表结果值。只有异步代码成功执行(没有抛出异常),then
方法传入的结果处理函数才会被调用。
- 无结果值的异步方法
printHelloWorld().then((value) => print("print hello world complete!$value"));
由于printHelloWorld
没有结果值,所以value
的值为null
。你可以直接用_
代替value
:
printHelloWorld().then((_) => print("print hello world complete!"));
- 有结果值的异步方法
provideOneInt().then((value) => print("I received a int:$value"));
then
方法的返回值为Future
类型。当传入的函数返回Future
类型时,then
直接返回该future
;当为其他类型值时,then
返回一个包裹该值Future
对象。
catchError
catchError
用于处理抛出的异常:
provideOneInt()
.then((value) => print("I received a int:$value"))
.catchError((ex) {
//handle the error
});
catchError
的返回值为Future
类型。当捕获到异常时,返回的Future
是异常处理函数返回的Future
或包裹异常处理函数返回值的Future
;当未捕获到异常时,catchError
原样传递Future
。
所以,catchError
的调用位置很重要。当在then方法后调用catchError,会捕获异步代码和结果处理代码抛出的异常。如果直接在原始Future上调用catchError,只会捕获异步代码抛出的异常,如:
provideOneInt().catchError((ex) {
//handle the error
}).then((value) => print("I received a int:$value"));
在上面的代码中,catchError
只能捕获provideOneInt
抛出的异常。而且当抛出异常时,then
处理的是错误处理函数的结果,而不是provideOneInt
返回的结果。如果没有抛出异常,由于cachError只会原样传递,所以处理的是t
provideOneInt
的结果。
whenComplete
then
方法传入的结果处理函数只在异步代码成功执行的情况下调用,catchError
方法传入的错误处理函数只在代码抛出异常的时候调用。whenComplete
传入的处理函数,不管是否抛出异常都会执行。相当于finally
。
provideOneInt()
.then((value) => print("I received a int:$value"))
.catchError((ex) {
//handle the error
}).whenComplete(() {
//finally code
});
多个异步方法的链式调用
await 关键字
当调用返回值为Future的异步方法时,如果不想使用Future提供的callback式的api,可以使用await关键字。
await关键字会导致当前方法等待,直至异步方法返回结果,当前方法才继续向下执行。
使用await关键字的前提是当前方法用async修饰。被async关键字修饰,且内部含有await表达式的方法本身是异步的,在方法内部,await 调用的异步方法会同步返回结果。
void printInt() async {
int value = await provideOneInt();
print("print the int:$value");
}
异常处理
void printInt() async {
try {
int value = await provideOneInt();
print("print the int:$value");
} catch (ex) {
//handle the error
} finally {
//finally code
}
}
stream vs await for
Stream<int> streamIntValues() {
return Stream<int>.fromIterable([1, 2, 3, 4, 5]);
}
void callbackMode() {
streamIntValues()
.listen((value) => print(value), onDone: () => print("on done."));
}
void awaitForMode() async {
await for (int value in streamIntValues()) {
print(value);
}
}