这可能是一个反复出现的问题,但我发现了相互矛盾的答案,现在我不知道哪一个是正确的。我以为我理解了这个概念,然后我开始阅读所有这些答案,我完全困惑了,所以我在寻找一个明确而简单的答案来回答我的问题,我很容易理解。
根据这个答案和这篇文章,wait
应该中断代码执行,实际上等待未来完成,然后继续按顺序执行其余代码。它还表明这可能会阻塞主线程,这在这种情况下是合乎逻辑的。
另一方面,来自flutter团队的这个、这个和这个视频表明wait
不会阻止其余的代码执行,注册回调以在未来完成时执行只是语法糖,这是然后
做的同样的事情。
现在,我试着编写一个小程序来理解其中哪一个是正确的,似乎第一种方法就是要走的路:
import 'dart:async';
// prints:
// 1000+
// 1000+
void main() async {
Stopwatch watch = Stopwatch();
watch.start();
await Future.delayed(Duration(seconds:1)).then((_){print(watch.elapsedMilliseconds);});
print(watch.elapsedMilliseconds);
}
反对:
import 'dart:async';
// prints:
// 0
// 1000+
void main() async {
Stopwatch watch = Stopwatch();
watch.start();
Future.delayed(Duration(seconds:1)).then((_){print(watch.elapsedMilliseconds);});
print(watch.elapsedMilliseconds);
}
所以我只想知道为什么Flatter团队和一些人建议,await
不会阻止代码执行,以及这个概念是如何工作的。
实际上你的两个函数都有相同的结果,让我更清楚地解释一下。。
当调用异步函数时,它们只是不阻塞要呈现的应用程序的另一部分。我们在内部执行的任何操作都将被延迟,但其余部分将按原样工作。
现在让我们来看看你的例子
import 'dart:async';
// prints 1000+
void main() async {
Stopwatch watch = Stopwatch();
watch.start();
await Future.delayed(Duration(seconds:1));
print(watch.elapsedMilliseconds);
}
在上面的示例中,您只是传递
延迟持续时间
,而不是回调
。因此,它将该部分的其余部分视为回调
,一旦持续时间
完成,就会调用该回调。现在你在做什么,你告诉你的函数等待一段时间,你已经提供了execute
进一步的代码。
所以结果是1000
在下面的例子中
import 'dart:async';
// prints:
// 0
// 1000+
void main() async {
Stopwatch watch = Stopwatch();
watch.start();
Future.delayed(Duration(seconds:1)).then((_){print(watch.elapsedMilliseconds);}); // prints 1000+
print(watch.elapsedMilliseconds); // prints 0
}
您正确地为Future分配了一个
回调
。所以现在,未来将只保留它的回调
,让其余部分完成。
这就是它先打印0,然后在延迟一秒钟后打印1000的原因。
未来和未来的延迟有不同的工作流程,这可能是现在使用等待或不等待的正确方式。
安德烈亚的回答在技术上是正确的。然而,在我能够理解它到底是如何工作的之前,我仍然需要对它进行很多思考,这就是为什么我会尝试为任何可能有相同问题的人简化事情。
假设你有一个飞镖计划;显然是使用main()
。我们的程序调用两个函数foo()
和bar()
。
函数foo()
做一些异步工作,例如网络调用:
Future<void> foo() async{
Stopwatch watch = Stopwatch();
watch.start();
await Future.delayed(Duration(seconds:1));
print(watch.elapsedMilliseconds);
}
函数bar()
是执行一些同步代码的正常函数:
void bar() {
print("Some synchronous code");
}
现在,假设您的main()
如下所示:
void main() {
foo();
bar();
}
主程序启动并调用foo()
-在main()
中没有wait
-我们点击foo()
中的wait
程序说:“哦!我不应该延迟main()
中的其余执行。当异步工作完成时,我必须注册一个要执行的回调,然后继续执行main()
"。foo()
从调用堆栈中弹出,然后调用bar()
并打印“一些同步工作”,也从调用堆栈中弹出。与此同时,foo()
中的异步工作完成并发出完成信号。这会被事件循环拾取,该事件循环返回执行foo()
中的其余代码(或者如果我们使用. then()
则回调中的代码);当然,如果主线程不忙。
简单地说就是发生了什么。正如Andrija所建议的,await
在同一个函数中阻止其余代码的执行;程序的其余部分将正常运行。如果我们使用main()
中的wait
来等待foo()
,那么main()
中的执行也会被阻止,直到foo()
中的异步工作完成,这不是我最初所想的。
我的想法是,main()
中的代码也会根据foo()
中的await
延迟,这与我们之前看到的情况不同。
我认为对阻塞有一点误解。当您查看第一个示例时——wait只会阻止函数中的其余代码执行。您的应用程序的其余部分仍然可以正常工作。
您需要理解一件事:异步/等待语法只是一种语法糖分。然后是(回调)语法。它们都实现了相同的功能,只有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前面放wait,这意味着我们将开始延迟的未来,但我们不会等待它的完成。
您将看到整个功能只需3秒钟即可完成;因为这三个计时器同时运行。将来等待将等待一个期货列表完成。
现在——很明显,你并不真的需要。then()语法在大多数情况下都适用,但我认为它仍然适用于更复杂的场景。
例如:您需要从3台服务器获取3个映像。这些服务器中的每台都有一个备份服务器;如果第一台服务器结果返回null-您需要从备份服务器获取资源。另外:如果备份服务器1或备份服务器2返回null,您需要调用服务器4来获取单个映像。
你甚至可以画一个小图表来描述这一点。这就是我要说的。然后()语法就派上了用场——我们仍然会将它与async/await结合起来。我认为,一旦你完全理解了这个例子,你就会非常理解async/await和。然后()。走吧:
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");
}
这里有一个新东西是:。然后()将返回一个未来的对象,您仍然可以使用wait关键字等待它。告诉过你这是同一件事。。。。
没有然后()语法,您需要再创建一个异步函数来处理这个问题,这会使代码有点复杂,更难阅读。具有then()语法代码更易于管理。再次看到-。那么()和async/await实际上是一回事。。。
当事情是线性的时,标准的async/await会有所帮助(比如我展示的multiple Future.delayed Example)。但当您看到一个复杂的场景时,可以通过多个分支并行运行的图来描述-。然后()就会派上用场。
如果Dart是单线程的,可以这样想:您的代码在Dart引擎(或Dart VM)中运行,而这段代码实际上是单线程的。但对外部世界的任何调用都将并行运行(调用远程服务器,甚至调用本地硬盘驱动器,调用同一主机上的其他进程,如操作系统——是的,甚至调用我的示例中的计时器)。
就像我上面的例子:我调用了3个远程服务器来获取一些东西,我链接了3个不同的回调,每次调用1个。而“外部世界的事情”——称为服务器——实际上是并行发生的。Dart的单线程简单地保证在任何给定时间点只执行我的代码的一行。
如果您来自Java背景,您就会知道Java同步多个线程有多难:这是代码经常中断的地方。在Dart中,您无需担心这一点。真正的性能优化是Dart VM之外发生的任何事情都真正并行运行——Dart会为您处理它。
现在这是如何工作的:事件循环。这是一个小飞镖引擎,可以跟踪您的所有远程服务器调用,并在准备就绪时回调您的-嗯,回调过程。事件循环负责您的代码一次处理一个请求......
我读到过,在中包含异步是async/await的反模式。下面的代码可以工作,但是我想知道如果没有中的,如何实现相同的结果。 谢了。
Async/await 是以更舒适的方式使用 promise 的一种特殊语法,同时它也非常易于理解和使用。 Async function 让我们以 async 这个关键字开始。它可以被放置在一个函数前面,如下所示: async function f() { return 1; } 在函数前面的 “async” 这个单词表达了一个简单的事情:即这个函数总是返回一个 promise。其他值将自动被
在第一章节,我们简要介绍了async/.await,并用它来构建一个简单的服务器。本章将更为详细讨论async/.await的它如何工作以及如何async代码与传统的 Rust 程序不同。 async/.await是 Rust 语法的特殊部分,它使得可以 yield 对当前线程的控制而不是阻塞,从而允许在等待操作完成时,其他代码可以运行。 async有两种主要的使用方式:async fn和asyn
用asyncio提供的@asyncio.coroutine可以把一个generator标记为coroutine类型,然后在coroutine内部用yield from调用另一个coroutine实现异步操作。 为了简化并更好地标识异步IO,从Python 3.5开始引入了新的语法async和await,可以让coroutine的代码更简洁易读。 请注意,async和await是针对coroutin
我已经复习了/,在复习了几篇文章之后,我决定自己测试一下。然而,我似乎无法理解为什么这不起作用: 控制台输出以下内容(节点V8.6.0): >外:“对象承诺” >内部:嘿那里 为什么函数内部的日志消息会在之后执行?我认为创建/的原因是为了使用异步任务执行同步执行。 有没有一种方法可以使用函数内部返回的值,而不用后面的?