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() { 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 后,新加了两个关键字,async 和 await,即使不用 Future 也能让异步编程看起来就像同步编程一样。
上面的例子改成使用 async 和 await:
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,接下来我们看一下 Stream,Stream 在函数式编程中非常常见。
Streams
什么是 Stream
一个 Stream 就是数据的一个异步序列(数据流),比如用户生成的事件或者从文件读取的数据。
怎么生成一个 Stream 呢,我们来看一下 yield 关键字。
yield
yield 在异步编程会生成一个 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 官方文档