await关键字只能在加上async关键字的方法里使用,而且执行完await的代码后会立即返回出方法,await下面的代码要等到主函数里面的非异步代码执行完毕后再执行。举个例子:
void main() {
getName1();
getName2();
getName3();
}
getName1() async {
await getStr(); /这里直接返回出函数了,下面的代码会在main函数执行完再继续执行
print('getName1');
}
getStr() {
print('getStr');
}
getName2() async{
await getName4(); /这里直接返回出函数了,下面的代码会在main函数执行完再继续执行
print('getName2');
}
getName3() {
print('getName3');
}
getName4() {
print('getName4');
}
这个例子最后输出的结果是:
getStr
getName4
getName3
getName1
getName2
Future的链式调用,可以把上一个方法中返回的结果当作下一个方法中的参数传递给下个方法。
void main() {
Future(() => futureTask()) /这里futureTask()返回 8
.then((i) => 'aaa$i') /这里的参数i就是 8
.then((m) => print(m)) /这里的m就是 aaa8
.then((_) => new Future.error('出错了'))
.catchError((e) => print(e), test: (Object o) {
print('catcherror');
return false; /这里return true不会报异常,return false依然会报异常。
}).whenComplete(
() => print('complete')); /不管catch中返回的true还是false,whenComplete总会执行。
}
futureTask() {
return 8;
}
虽然叫做异步,但同时new出多个Future时并不是一起执行,而是有先后顺序去执行的。看下面一个经典例子:
void main() {
testFuture();
}
void testFuture() {
Future f1 = Future(() => print('1'));
Future f2 = Future(() => null);
Future f3 = Future(() => null);
Future f4 = Future(() => null);
f4.then((_) => print('2'));
f3.then((_) {
print('3');
Future(() => print('4'));
f2.then((_) => print('5'));
});
f2.then((_) => print('6'));
print('7');
}
最后的输出结果:7 1 6 3 5 2 4
你品你细品,反正我第一次看到这结果满头的问号。首先,单线程里面的异步操作肯定是放到最后执行,先执行完非异步的代码再执行异步的。梳理一下逻辑,进入到testFuture函数里面后,Future中的任务肯定是放到后面执行的,所以最后一行“print(‘7’)”肯定最先执行,然后按照先后new出Future的顺序,将Future加入到了事件队列里面,依次执行里面的函数,首先执行的 f1 里面的函数输出了“1”,然后执行 f2 里面的函数输出了“6”,然后执行 f3 里面的输出了“3”,此时第14行又new出了一个Future,依然是添加到事件队列里面排队,这是最后添加的所以排在最后,在第15行又调用了 f2.then方法,因为 f2 本来已经执行完了,这时再次调用会把新添加的任务放入 微任务队列 里面,而微任务队列执行的优先级是高于事件队列的,所以输出了“5”, 再然后就轮到 f4 了,输出“2”,再然后轮到最后第14行new出的Future中的任务执行,输出“4”,到这就全部执行完了。
微任务队列执行优先级高于Future,看一个和Future配合使用的经典例子:
void main() {
testScheduleMicrotask1();
}
void testScheduleMicrotask1() {
scheduleMicrotask(() => print('1')); //微任务
//delay延迟
Future.delayed(Duration(seconds: 1), () {
print('2');
});
Future(() => print('3'))
.then((_) {
print('4');
scheduleMicrotask(() => print('5'));
})
.then((_) => print('6'))
.then((_) => print('7'));
Future(() => print('8'));
scheduleMicrotask(() => print('9'));
print('10');
}
输出结果为:10 1 9 3 4 6 7 5 8 2
分析一下,"10"肯定是最先输出的,然后是第6行和第19行两个微任务输出“1”和“9”,第8行的Future设置了延迟1秒,肯定是最后输出,所以接下来执行的是第11行的Future,输出“3”“4”,需要注意的是第14行又new出了一个微任务,但是由于当前的Future还没有执行完,所以并不会立即执行微任务,还是要继续执行当前的Future,输出“6”“7”,然后再执行微任务,输出“5”,然后是“8”和“2”。
这里有个需要注意的地方,在Future.then()里面new 一个Future会先阻塞掉当前的Future的执行,会先执行后面的Future。
void main() {
futureTest2();
}
void futureTest2() {
scheduleMicrotask(() => print('1')); //微任务
//delay延迟
Future.delayed(Duration(seconds: 1), () => print('2'));
Future(() => print('3'))
.then((_) => Future(() => print('4'))) /注意在then里new一个Future会阻塞在这里,先执行后面的Future
.then((_) {
print('5');
scheduleMicrotask(() => print('6'));
}).then((_) => print('7'));
Future(() => print('8'))
.then((_) => Future(() => print('9'))) /注意在then里new一个Future会阻塞在这里,先执行后面的Future
.then((_) => print('10'));
Future(() => print('11'));
scheduleMicrotask(() => print('12'));
print('13');
}
输出结果:13 1 12 3 8 11 4 5 7 6 9 10 2
经测试发现delayed延时是从调用了delayed方法开始计时,而且多个Future.delayed计时不相互影响。测试代码:
void main() {
testScheduleMicrotask();
}
void testScheduleMicrotask() {
int t1 = DateTime.now().millisecondsSinceEpoch;
//delay延迟
Future.delayed(Duration(seconds: 4), () {
print('2');
print(
'--输出 2 相隔时间:' + (DateTime.now().millisecondsSinceEpoch - t1).toString());
});
Future.delayed(Duration(seconds: 3), () {
print('3');
print('--输出 3 相隔时间:' +
(DateTime.now().millisecondsSinceEpoch - t1).toString());
});
Future.delayed(Duration(seconds: 3), () {
print('4');
print('--输出 4 相隔时间:' +
(DateTime.now().millisecondsSinceEpoch - t1).toString());
});
print('5');
}
输出结果:
5
3
–输出 3 相隔时间:3009
4
–输出 4 相隔时间:3012
2
–输出 2 相隔时间:4005
方法用 sync* 来修饰
void main() {
var iterator = getSyncGenerator(5).iterator;
while (iterator.moveNext()) {
print(iterator.current);
}
}
//同步生成器 用sync*关键字
Iterable<int> getSyncGenerator(int n) sync* {
int k = n;
print('start');
while (k > 0) {
yield k--;
}
print('end');
}
输出结果:start 5 4 3 2 1 end
当调用getSyncGenerator方法后并不会立即执行方法里面的代码,直接返回一个Iterable,当调用iterator.moveNext()时才开始执行getSyncGenerator()里面的代码,执行到yield 时返回到第4行while循环体里面执行循环体里面的代码,执行完循环体里的代码又再次回到第13行的yield执行 k- -,执行完后再次回到第4行循环体,直到k==0,输出“end”,iterator.current返回的就是yield后面的 k。
方法用 async* 修饰
void main() {
StreamSubscription subscription=getAsyncGenrator(5).listen(null);
subscription.onData((value){
print('main '+value.toString());
if(value>2){
subscription.pause();
}
});
}
//异步生成器 用async*关键字
Stream<int> getAsyncGenrator(int n) async*{
int k=0;
print('start');
while(k<n){
yield k++;
print('yield '+k.toString());
}
print('end');
print('end2');
}
输出结果:
start
main 0
yield 1
main 1
yield 2
main 2
yield 3
main 3
yield 4
与同步生成器不同的是一旦调用 getAsyncGenrator(5).listen(null) 方法后,不管设置不设置监听,getAsyncGenrator()方法里面的代码都会开始执行,用subscription.onData可以来监听生成器里面 yield 后面 k 的变化,而且当调用 subscription.pause(); 后执行完第15行while的当前一次循环就会停止执行,可以看到连后面的“end”“end2”都没有打印出来。
void main() {
var iterator = getSyncGeneratorRecursion(5).iterator;
while (iterator.moveNext()) {
print(iterator.current);
}
}
//递归生成器
Iterable<int> getSyncGeneratorRecursion(int n) sync* {
print('start');
if(n>0){
yield n;
yield* getSyncGeneratorRecursion(n-1);
}
print('end');
}
输出结果:
start
5
start
4
start
3
start
2
start
1
start
end
end
end
end
end
end
Isolate是真正的开了一个线程,但是和Java的线程有很大区别,dart里面线程与线程间内存不共享,它们之间传递数据是用ReceivePort类来完成的。
Isolate最常用的是用 spawn()方法来创建一个线程
void main() {
var receivePort = new ReceivePort();
Isolate.spawn(aa, receivePort.sendPort);
Isolate.spawn(bb, receivePort.sendPort);
receivePort.listen((message) { /设置监听接收消息
switch (message[0]) {
case 1:
print('main:' + message[1]);
receivePort.close();
break;
case 2:
print('main:' + message[1]);
receivePort.close();
break;
}
});
sleep(Duration(milliseconds: 500));/主线程停500毫秒
receivePort.close();/关闭接收消息,这里关闭后,上面的监听代码也就收不到消息了
print('main关闭了监听');
sleep(Duration(seconds: 5));/主线程停5秒
}
void aa(SendPort port) {
for (int i = 0; i < 10; i++) {
sleep(Duration(milliseconds: 100));
print('aa:' + i.toString());
}
port.send([1, 'aa执行完毕']);/给主线程发送消息
}
void bb(SendPort port) {
for (int i = 0; i < 5; i++) {
sleep(Duration(milliseconds: 100));
print('bb:' + i.toString());
}
port.send([2, 'bb执行完毕']);/给主线程发送消息
}
输出结果:
bb:0
aa:0
bb:1
aa:1
aa:2
bb:2
aa:3
bb:3
main关闭了监听
aa:4
bb:4
aa:5
aa:6
aa:7
aa:8
aa:9
在调用Isolate.spawn()方法后,新的线程就会执行起来,主线程这边可以设置接收消息的监听,而且关闭监听也不会影响新线程的执行,只要主线程还没有退出,新线程就会一直执行完。