我认为对阻塞存在一些误解。当您查看您的第一个示例时 - await 将仅阻止执行 您的函数中的其余代码。您的应用程序的其余部分仍然可以正常工作。
您需要了解一件事:async/await 语法只是 .then(callback) 语法的语法糖。它们都实现了相同的目标,只有 async/await 更容易阅读、调试和理解。如您所见-在您的两个示例中,您都得到了相同的结果。您的问题是:您更喜欢哪种语法?
为了澄清 - 让我们假设您要引入几个 1 秒的等待事件,并在每个事件之后写一条消息。
您的第一个示例将如下所示:
import 'dart:async';
// prints 1000+
void main() async {
Stopwatch watch = Stopwatch();
watch.start();
await Future.delayed(Duration(seconds:1));
print(watch.elapsedMilliseconds);
await Future.delayed(Duration(seconds:1));
print(watch.elapsedMilliseconds);
await Future.delayed(Duration(seconds:1));
print(watch.elapsedMilliseconds);
}
请注意阅读和理解代码是多么容易。
现在,把第二个例子改成同样的事情:
import 'dart:async';
void main() async {
Stopwatch watch = Stopwatch();
watch.start();
Future.delayed(Duration(seconds:1)).then((_){
print(watch.elapsedMilliseconds);
Future.delayed(Duration(seconds:1)).then((_){
print(watch.elapsedMilliseconds);
Future.delayed(Duration(seconds:1)).then((_){
print(watch.elapsedMilliseconds);
});
});
});
}
它们都会达到相同的效果 - 但第二个例子会让你的眼睛受伤。
您需要考虑的一个更有趣的场景是 - 如果您希望同时发生几件事情怎么办?这并不罕见 - 如果您需要从 3 个不同的服务器获取 3 个图像,您不会按顺序获取它们。您可能希望同时触发所有 3 个请求,并等待它们全部完成。
使用 async/await 这很容易:
import 'dart:async';
// prints 1000+
void main() async {
Stopwatch watch = Stopwatch();
watch.start();
var f1 = Future.delayed(Duration(seconds:1));
var f2 = Future.delayed(Duration(seconds:2));
var f3 = Future.delayed(Duration(seconds:3));
await Future.wait([f1, f2, f3]);
print(watch.elapsedMilliseconds);
}
请注意,由于我们没有在每个 Future.delayed 前面放置 await - 这意味着我们将启动延迟的未来,但我们不会等待它完成。
你会看到整个功能只需要3秒就可以完成;因为所有 3 个计时器同时运行。 Future.wait 将等待期货列表完成。
现在 - 很明显,在大多数情况下您并不真正需要 .then() 语法,但我认为它仍然适用于更复杂的场景。
例如:您需要从 3 个服务器获取 3 个图像。这些服务器中的每一个都有一个备份服务器;如果第一台服务器返回 null 作为结果 - 您需要从备份服务器获取资源。
另外:如果Backup server 1或Backup server 2返回null,则需要调用server 4获取单张图片。
您甚至可以绘制一个小图来描述这一点。现在这就是 .then() 语法派上用场的地方——我们仍然会将它与 async/await 结合起来。我想一旦你完全理解了这个例子——你就会非常理解 async/await 和 .then()。走吧:
import 'dart:async';
import 'dart:math';
Future<int?> getImage(String server) async {
var rng = Random();
print("Downloading from $server");
// we'll add random delay to simulate network
await Future.delayed(Duration(seconds: rng.nextInt(5)));
print("$server is done");
// high chance of returning null
if (rng.nextInt(10)<7) return null;
return 1;
}
// prints 1000+
void main() async {
Stopwatch watch = Stopwatch();
watch.start();
// get the image from server 1
var f1 = getImage("Server 1").then((data) async {
return data ?? await getImage("Server 1 backup");
});
var f2 = getImage("Server 2").then((data) async {
return data ?? await getImage("Server 2 backup");
});
var f4=Future.wait([f1, f2]).then((data) async {
if (data[0]==null || data[1]==null) {
return [await getImage("Server 4")];
} else {
return data;
}
});
var f3 = getImage("Server 3").then((data) async {
return data ?? await getImage("Server 3 backup");
});
await Future.wait([f3, f4]);
print("elapsed ${watch.elapsedMilliseconds} ms");
}
这里有一个新的东西:.then() 将返回一个未来对象 - 你仍然可以使用 await 关键字等待。告诉过你是一样的......
如果没有 .then() 语法,您将需要再创建一个异步函数来处理此问题,这会使您的代码有点复杂且更难以阅读。使用 .then() 语法,代码更易于管理。再看一遍 - .then() 和 async/await 实际上是一回事......
当事情是线性的时,标准的 async/await 会有所帮助(比如我展示的多个 Future.delayed 示例)。但是,当您遇到可以通过多个并行运行的分支的 Graph 来描述的复杂场景时,.then() 将派上用场。
编辑 - Dart 是单线程
关于 Dart 是单线程的,这样想:你的代码在 Dart 引擎(或 Dart VM)中运行,而这段代码确实是单线程的。但是对外部世界的任何调用都将并行运行(调用远程服务器,甚至调用本地硬盘驱动器,调用同一主机上的其他进程,如 OS - 是的,甚至像我的示例中那样调用计时器)。
就像我上面的例子:我调用了 3 个远程服务器来获取一些东西,我链接了 3 个不同的回调,每个调用 1 个。而“外界事物”——调用服务器——实际上是同时发生的。 Dart 的单线程只是保证在任何给定时间点我的代码中只有一行代码会被执行。
如果您有 Java 背景,您就会知道在 Java 中同步多个线程是多么困难:这也是代码经常中断的地方。在 Dart 中,您无需担心这一点。真正的性能优化在于,在 Dart VM 之外发生的任何事情实际上都是并行运行的 - Dart 会为您处理。
现在这是如何工作的:事件循环。这是一个小型 dart 引擎,可以跟踪所有远程服务器调用,并在准备好时回调您的 - 好吧,回调过程。事件循环是负责您的代码同时处理一个请求的一种......