Skip to main content

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 单元素异步处理asyncawait

asyncawait本质上是对一个回调嵌套的函数进行同步写法,从而避免了回调地狱的情况。如:

  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*, yieldawait

在一个函数内使用async*, yieldawait,那么这个函数必然返回一个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*, yieldawait,唯一的区别可以看成是在yield*并入了一个流.然后先处理完这个流,再执行一个yieldyield*

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));
});