我正在开发一个需要使用本地存储的flutter应用程序。由于Android中的文档目录路径将在Future<目录>,每次我想使用路径时,我都要检查未来是否结束。
该代码可能类似于下面的
class DataStructure {
late Future<Directory> _dir = getApplicationDocumentsDirectory();
Future<void> read(String fileName) async {
Directory dir = await _dir;
// Do something using dir
}
}
这可能很愚蠢,但我大部分时间都在编写C++代码,所以我想减少将函数推送到事件队列的次数,以获得更好的性能(我猜编译器会将带有await
的异步函数切割为"函数",以便推送到事件列队)。所以我写了实验代码来检查等待一个完成的未来是否会切断控制流。(我的意思是,在最新版本的Dart中,异步函数将执行,直到出现第一个"等待"关键字。我想知道如果未来已经结束,情况是否仍然如此。)
Future<void> voidFuture(String name) async {
print('$name: voidFuture');
}
Future<void> printSingle(String name, int index) async {
print('$name: -- index = $index');
}
// This emit function will print one line each time
// it gets chance to be executed.
// Because the 'await' keyword will cut the control flow,
// it can only print once each time it's executed,
// and must wait for the next chance to print again.
// This feature makes it appropriate for testing
// when and where the control flow is cut,
// as each cut will lead to one line of output.
Future<void> emit(String name, int count) async {
for (int index = 0; index != count; ++index) {
await printSingle(name, index);
}
}
Future<void> task_0() async {
const String name = 'task_0';
Future<void> emitFinish = emit(name, 3);
await voidFuture(name);
print('$name: output after await');
await emitFinish;
}
在我的环境中运行task_0(Dart SDK版本:2.18.5(稳定);windows_x64")输出如下:
task_0: -- index = 0
task_0: voidFuture
task_0: -- index = 1
task_0: output after await
task_0: -- index = 2
这和我预想的一样。当我改变emit()
函数时,奇怪的事情来了:
Future<void> emit(String name, int count) async {
for (int index = 0; index != count; ++index) {
// Before:
// await printSingle(name, index);
// After:
await Future(() {
print('$name: -- index = $index');
});
}
}
然后输出变成
task_0: voidFuture
task_0: output after await
task_0: -- index = 0
task_0: -- index = 1
task_0: -- index = 2
第三行-- index = 0
在output after await
之后对我来说毫无意义。函数似乎比构造函数的未来更有特权?
我的主要问题是";"等待"会等待一个完整的未来吗&";,所以我写了下面的代码:
Future<String> stringFuture() async {
return '.';
}
Future<void> task_3() async {
const String name = 'task_3';
Future<void> emitFinish = emit(name, 4);
Future<String> futureString = stringFuture();
print('$name: before await of futureString');
await futureString;
print('$name: 1st await of futureString over');
await 1;
print('$name: 1st await of constant over');
await 2;
print('$name: 2nd await of constant over');
await emitFinish;
}
对于emit()
的第一个版本,输出为
task_3: -- index = 0
task_3: before await of futureString
task_3: -- index = 1
task_3: 1st await of futureString over
task_3: -- index = 2
task_3: 1st await of constant over
task_3: -- index = 3
task_3: 2nd await of constant over
这意味着即使是常数积分的await
也会将其后面的行推送到事件队列。
(当然,对于emit()
的第二个版本,它的所有输出都在task_3()
中的最后一个print()
之后,我不知道为什么)
我知道有很多解决办法,其中之一是在第一次Future<T>
完成后使用T? value
进行分配,并检查每次使用value == null
是否正确。
但我想问的问题是:
关键字
await
在内部做什么?请提供足以解释上述现象的细节。是否有任何方法可以覆盖默认的
await
行为?例如,通过重写一个方法?多次使用
Future
值的首选方式是什么?(与上述无关)如何在Flutter中的欢迎页面停止以等待这些异步功能,例如
getApplicationDocumentsDirectory()
在构建所有小部件之前完成
我从Google获得的大多数结果都是为初学者介绍async
和await
关键字,在Dart API参考文档中找不到太多解释await
行为的材料。
谢谢你救了一颗被await
>_<;
await
是通过Future
API注册回调的语法糖。例如:
Future<int> foo() async {
var x = await someIntFuture();
return otherStuff(x);
}
基本上转化为:
Future<int> foo() {
return someIntFuture.then((x) {
return otherStuff(x);
});
}
CCD_ 22注册CCD_ 23回调并返回该新的CCD_。它返回Future
意味着await
总是产生(即使在await null
这样的情况下)。这也是为什么当您调用async
函数时,它的主体会被同步执行,直到它到达它的第一个await
。
第三行
-- index = 0
在output after await
之后对我来说毫无意义。函数似乎比构造函数的未来更有特权?
来自Future
构造函数的文档:
创建包含与
Timer.run
异步调用computation
的结果的future。
提供给Future
构造函数的回调是异步调用的;它被安排好了。这与调用async
函数不同,后者如前所述,首先尽可能同步地执行。
我的主要问题是"等待"会等待一个完整的未来吗">
Future
是否已完成并不重要。CCD_ 38总是产生。
是否有任何方法可以覆盖默认的
await
行为?例如,通过重写一个方法?
如前所述,await
是句法糖。您可以做的是创建一个实现Future
API并以不同方式处理.then
的类(这就是Flutter的SynchronousFuture
类所做的),但我不推荐使用它(原因与SynchronousFuture
文档不鼓励使用它的原因相同)。
多次使用
Future
值的首选方式是什么?
取决于具体情况。通常,尝试await
和Future
一次,并将结果存储在某个地方(例如本地变量中)。否则,我只会多次await
和Future
,在有证据表明它对性能至关重要之前,我不会担心它。
(与上面无关)如何在Flutter中的欢迎页面停止等待这些异步功能
取决于。对于某些情况,您可以简单地使main
函数async
和await
在调用runApp
之前进行任何异步初始化。在其他情况下(特别是对于长时间运行的情况),应该使用FutureBuilder
。
(此外,今后应单独提出问题。)