Dart中的异步编程

Dart中的异步

最近在写Flutter,结果发现Dart的异步编程还和我目前学过的都不太一样,简单总结一下。

Future

什么是 Future

Future 就是一个Future<T> 对象,代表就是一个异步操作返回的结果,类型是T
如果返回的是不可用的值,则应该为Future<void>

举个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

void main() {
print('test1:${DateTime.now()}');
testDelay();
print('test2:${DateTime.now()}');
}

testDelay() {

Future<String> f = testFuture();
f.then((str){
print(str);
});
print('testDelay:${DateTime.now()}');
}

Future<String> testFuture() {
// 延迟3秒
return Future.delayed(Duration(seconds: 3),() {
return 'future:${DateTime.now()}';
});
}

输出结果

1
2
3
4
5
test1:2019-02-03 03:03:41.206838
testDelay:2019-02-03 03:03:41.226762
test2:2019-02-03 03:03:41.227339
三秒后...
future:2019-02-03 03:03:44.227833

这就是Future的用法,其中用到了then,如果需要使用链式编程,可以这样

1
2
3
4
expensiveA()
.then((aValue) => expensiveB())
.then((bValue) => expensiveC())
.then((cValue) => doSomethingWith(cValue));

async / await

Dart在1.9后,新加了两个关键字,asyncawait,即使不用Future也能让异步编程看起来就像同步编程一样

上面的例子改成使用asyncawait

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void main() {
print('test111:${DateTime.now()}');
testDelay();
print('test2${DateTime.now()}');
}

testDelay() async {
print('testDelay:${DateTime.now()}');
String str = await testFuture();
print(str);
}

Future<String> testFuture() {
return Future.delayed(Duration(seconds: 3),() {
DateTime time = DateTime.now();
return 'future:$time';
});
}

同样的输出效果
就相当于直接从Future获取到了值,这样与原来不同的是,在await执行后的代码都被延时后执行,这样也可以使用await来保证执行顺序

1
2
3
4
5
main() async {
await expensiveA();
await expensiveB();
doSomethingWith(await expensiveC());
}

保证了执行顺序是 A -> B -> C

Dart中的异步编程除了Future还有Stream,接下来我们看一下StreamStream在函数式编程中非常常见

Streams

什么是Stream

一个Stream就是数据的一个异步的序列(数据流),序列用户生成的事件或者从文件读取的数据

怎么生成一个stream呢,我们来看一下yield关键词

yield

yeild在异步编程会生成一个stream,需要用async*来标识,yield的功能可以理解成一个叠加器,会不断累积前面的结果,加入新的数据,最后返回一个包含所有结果的容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Stream<int> testYield() async* {
for (int i = 0; i < 15;++i) {
yield i;
}
}

// 要使用 `await for` 必须要在异步函数中
test() async {
Stream<int> st = testYield();
// 使用 await for 遍历stream的内容
await for (int item in st) {
print(item);
}
}

如果在yield在同步方法中使用会怎么样,那生成的就不是stream,而是一个Iterable对象

Iterable

使用yield必须用sync*来标识,然后不能在

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
test() {
Iterable it = testYield();
it.forEach(print);

// 也可以用for来遍历
// for (var item in it) {
// print(item);
// }
}

Iterable testYield() sync* {
for (int i = 0; i < 15;++i) {
yield i;
}
}

yield*

如果我们要使用递归,对yield的结果进行yield会怎么样,看这个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void main() {
test();
}

test() {
Iterable it = testYield(10);
it.forEach(print);
}

testYield(int n) sync* {
if (n > 0) {
yield n;
yield testYield(n - 1);
}
}

输出结果

1
(9, (8, (7, (6, (5, (4, (3, (2, (1, ())))))))))

这并不是我们想要的结果,看来对yield的结果进行yield则是直接把yield生成的容器进行了yield,我们需要把yield生成容器的结果进行累加,则需要使用 yield*

修改为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void main() {
test();
}

test() {
Iterable it = testYield(10);
it.forEach(print);
}

testYield(int n) sync* {
if (n > 0) {
yield n;
yield* testYield(n - 1);
}
}

这样就能达到我们想要的结果了。

参考

dart官方文档