Dart 中的异步编程

lxf2023-05-05 05:26:01

同步和异步编程

向孩子解释,同步编程意味着一次做一件事情,一步一步,等待每一步完成,然后再继续下一步。这就像一次一页地做家庭作业,直到完成您正在做的作业才转到下一页。

另一方面,异步编程意味着同时做事,而不是等到每个步骤完成后再开始下一个步骤。这就像一边听音乐一边画画,而不必为了做另一项而停止一项活动。

在 Dart 中,我们可以编写同步或异步工作的程序,这取决于我们需要做什么。同步编程适用于可以快速完成的简单任务,而异步编程适用于需要很长时间或需要在我们做其他事情时在后台运行的任务。

所以,当我们编写 Dart 程序时,我们可以选择是一次做一件事情(同步)还是同时做一件事情(异步),这取决于我们想要实现什么。

什么是Future?

Future 在 dart 中用于表示要完成或未完成的操作。如果任务完成,我们会得到一个结果,否则,我们会返回一个错误。在异步编程中,我们强制长时间处理的任务遵循有序路径。

void main() {
  print('Fetching user data...');
  var userData = fetchUserData();
  print('User data received: $userData');
  print('End of main');
}

Future<String> fetchUserData() {
  // Imagine that this function is more complex and slow.
  return Future.delayed(
    Duration(seconds: 3), 
    () => 'John Doe'
  ); // simulate network delay
}
Output:

Fetching user data...
User data received: Instance of '_Future<String>'
End of main

从上面的代码来看,该fetchUserData()函数返回一个Future对象,该对象将在延迟 3 秒后以网络请求的结果完成,由该Future.delayed()方法表示。在这种情况下,结果只是一个包含名称“John Doe”的字符串

输出显示该main函数是同步执行的,将第一条消息打印到控制台。

接下来,fetchUserData()调用该函数,它返回一个Future<String>对象。返回的未来被分配给userData变量。

然后将第二条消息打印到控制台,其中显示变量的值userData。由于userData包含一个Future<String>对象,输出显示“Instance of '_Future'”而不是实际的用户数据。

最后,最后一条消息被打印到控制台,表示 main 函数结束。

出现此输出的原因是该fetchUserData()函数返回一个Future<String>对象,该对象表示异步操作的最终结果。当我们将这个未来对象分配给一个变量并尝试立即打印它时,我们看到的是对象的类型而不是它的值。

要访问对象的值Future,我们需要使用.then()方法或函数await中的关键字async

使用 .then() 方法

.then()方法用于注册将在Future对象完成时调用的回调函数。

当我们有一个Future表示将在未来某个时间点完成的异步操作的对象时,我们可以使用该.then()方法附加一个回调函数,该回调函数将在未来得到结果时执行。

void main() {
  print('Fetching user data...');
  fetchUserData().then((userData) {
    print('User data received: $userData');
    print('End of main');
  });
}
Output:

Fetching user data...
User data received: John Doe
End of main

在此示例中,我们使用 方法.then()注册一个回调函数,以便在返回的 FuturefetchUserData()完成时调用。回调函数将userData字符串作为其参数,然后将其与另一条指示主函数结束的消息一起打印到控制台。

请注意,在上面的示例中,print()调用后的语句fetchUserData()已被删除,因为我们现在使用.then()方法访问结果,Future而不是将其分配给变量并直接打印。

如果我们在调用print()之后放置怎么办fetchUserData()

void main() {
  print('Fetching user data...');
  fetchUserData().then((userData) {
    print('User data received: $userData');
  });
  print('End of main');
}
Output:

Fetching user data...
End of main
User data received: John Doe

第三条消息(“End of main”)在 完成之前被打印出来Future,因为对 的调用会立即fetchUserData()返回一个Future,并且该.then()方法用于注册一个回调函数,该回调函数将在Future完成后执行。

这个例子展示了我们如何使用该.then()方法在异步操作的结果可用时执行一些操作,而不会阻塞主线程。

.then()对于有序执行,您需要在检索后将打印放在回调中userData

使用异步/等待

和关键字用于编写看起来同步且更易于阅读和理解的异步代码,而不会阻塞主线程asyncawait

void main() async {
  print('Fetching user data...');
  var userData = await fetchUserData();
  print('User data received: $userData');
  print('End of main');
}
Output:

Fetching user data...
End of main
User data received: John Doe

关键字async用于将函数标记为异步,这允许我们在函数内部main使用关键字来暂停其执行,直到 a完成并返回结果。await``Future

在这种情况下,当 await 关键字与fetchUserData()函数调用一起使用时,它会暂停函数的执行main,直到返回的 FuturefetchUserData()得到结果解析。

Future完成后,await关键字将结果分配给userData变量,函数恢复执行main,将第二条消息打印到控制台。

以这种方式使用asyncandawait可以更轻松地编写和阅读异步代码,因为它看起来类似于同步代码,并且可以清楚地了解代码的流程。它还可以更轻松地处理异步操作可能引发的错误和异常,因为它们可以像try-catch在同步代码中一样使用块来捕获。

结论

在 Dart 中,异步编程可以使用对象来实现Future,对象代表一个可能还不可用的值,和async关键字await可以让我们编写看起来同步且更易于阅读和理解的异步代码。