达特内部究竟在等待什么



我正在开发一个需要使用本地存储的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 = 0output 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获得的大多数结果都是为初学者介绍asyncawait关键字,在Dart API参考文档中找不到太多解释await行为的材料。

谢谢你救了一颗被await&gt_&lt;

await是通过FutureAPI注册回调的语法糖。例如:

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 = 0output after await之后对我来说毫无意义。函数似乎比构造函数的未来更有特权?

来自Future构造函数的文档:

创建包含与Timer.run异步调用computation的结果的future。

提供给Future构造函数的回调是异步调用的;它被安排好了。这与调用async函数不同,后者如前所述,首先尽可能同步地执行。

我的主要问题是"等待"会等待一个完整的未来吗">

Future是否已完成并不重要。CCD_ 38总是产生。

是否有任何方法可以覆盖默认的await行为?例如,通过重写一个方法?

如前所述,await是句法糖。您可以做的是创建一个实现FutureAPI并以不同方式处理.then的类(这就是Flutter的SynchronousFuture类所做的),但我不推荐使用它(原因与SynchronousFuture文档不鼓励使用它的原因相同)。

多次使用Future值的首选方式是什么?

取决于具体情况。通常,尝试awaitFuture一次,并将结果存储在某个地方(例如本地变量中)。否则,我只会多次awaitFuture,在有证据表明它对性能至关重要之前,我不会担心它。

(与上面无关)如何在Flutter中的欢迎页面停止等待这些异步功能

取决于。对于某些情况,您可以简单地使main函数asyncawait在调用runApp之前进行任何异步初始化。在其他情况下(特别是对于长时间运行的情况),应该使用FutureBuilder

(此外,今后应单独提出问题。)

最新更新