06 Dart异步和同步关键字应用
1 sync* 和yield 多元素同步迭代使用
1.1 sync* 和 yield的元素组同步迭代
在dart中的yield应用于方法或函数中,起到暂停的作用,加上关键字sync*后,则返回一个Iterior类型。而Iterior的
成员取决于方法内有多少个yield暂停.
group('keyword yield and sync* Test ', () {
test('Length test', () {
Iterable<String> getItems() sync* {
yield 'hello';
yield 'world';
}
print(getItems().length); // 2
});
});
1.2 代码迭代和for遍历的区别
所以有当多个元素中进行同步迭代时,能执行到多少个yield则算多少个元素。
那么这样的来写代码有什么用?能解决什么样场景的问题?假设一个有限集合,而我们要找的其中的一个元素就在其中,而每读取一个元素,意味着,
计算机运行到下一个yield并返回当前yield的结果。我们可以通过这一结果再决定要不要接送执行下去。 其运行的结果同for遍历一样。如,
test('Foreach Iterable', () {
Iterable<String> getItems() sync* {
yield 'hello';
yield 'world';
}
getItems().forEach(print);
// hello
// world
});
结果很明显,这种遍历for也是可以实现同样的效果,那为什么要用迭代器呢?使用迭代器相对于for遍历,可以有效去控制内存。如for遍历列表时,需要
把数据都加载到内存中,然后进行遍历,假设一个列表,里面有n个元素,那么,使用for来遍历的话,只能把数据都加载进来,再进行遍历,而使用yield来
处理的话,就可以每要把执行下一个yield则,这里的处理逻辑则可以写成以游标的方式去读取该条数据,然后返回结果并暂停。相对来说,占用内存可以更少。如:
test('Foreach Iterable', () {
// 假设这是从文件中读取一个单词出来
String readFromFile(int index) => ['hello', 'wolrd'][index];
Iterable<String> getItems() sync* {
for (var index = 0; index < 2; index++) {
yield readFromFile(index);
}
}
getItems().map(prints);
// hello
// world
});
所以相交于for的一次性遍历需要加载全部的数据,Iterable可以通过一个游标的方式一个一个去读取数据,从而不会一开始就占用过多的内存。
2 sync* 和yield*多元素同步迭代使用
yield*关键字后接Iterable,可以理解成,yield*后面对接的就是另一个迭代集,如果把函数中的yield比作是一个元素的话,那么yield*就是把一个整个
元素集拿过来到这个函数中进行一个一个迭代。
test('Test for sync* yield*', () {
Iterable<String> getEmojiWithTime(int count) sync* {
Iterable<String> getItems() sync* {
yield 'hello';
yield 'world';
}
yield* getItems();
yield '!';
}
print(getEmojiWithTime(10).join(' ')); // hello world !
});
3 单元素异步处理async 和await
async和await本质上是对一个回调嵌套的函数进行同步写法,从而避免了回调地狱的情况。如:
test( 'async await test', () async {
Future.delayed(const Duration(seconds: 1)).then((value) {
print('OK');
});
await Future.delayed(Duration(seconds: 3));
},
);
这2种写法的作用是一样的,不过,用await的语法糖方式避免了嵌套的写法,给人一种串行的代码形式,其实本质还是以回调的方式实现异步的,只是通过await
语法糖形式简化了写法,避免开发者不必要的心理负担。
4 多元素异步处理: async*, yield和await
在一个函数内使用async*, yield和await,那么这个函数必然返回一个Stream<T>对象.其结果同多元素同步处理的一样,只不过返回的是Stream<T>对象。
test('Multiple async test', () async {
Future<String> getWord() async => 'Hello world!';
Stream<String> getStream() async* {
yield await getWord();
}
getStream().listen(print);
await Future.delayed(const Duration(seconds: 3));
});
5 多元素异步嵌套处理: async*, yield*和await
其实这个跟上面的async*, yield和await,唯一的区别可以看成是在yield*并入了一个流.然后先处理完这个流,再执行一个yield或yield*
test('Multiple async* test', () async {
Stream<String> getWord() async* {
yield await Future(() => 'Hello world!');
yield await Future(() => 'Hello world!');
}
Stream<String> getStream() async* {
yield* getWord();
}
getStream().listen(print);
await Future.delayed(const Duration(seconds: 3));
});