目录
Contents
本章的重点
What's the point?
-
Dart代码运行在单个执行“线程”中。
Dart code runs in a single “thread” of execution.
-
阻塞执行线程的代码会使你的程序“冻结”。
Code that blocks the thread of execution can make your program freeze.
-
一个
Future
对象用于表示 异步操作 的结果,这些正在处理的操作或 I/O 将会在稍后完成。Future
objects (futures) represent the results of asynchronous operations — processing or I/O to be completed later. -
在异步函数中使用
await
关键字暂停代码的执行,直到对应的 future 完成。To suspend execution until a future completes, use
await
in an async function. -
可以使用 try-catch 表达式来捕获异步函数中代码的执行错误。
To catch errors, use try-catch expressions in async functions.
-
为了可以使不同的代码片段同时执行,可以为它们创建一个 isolate(如果你的程序是一个 Web 应用,则创建 worker 替代 isolate)
To run code concurrently, create an isolate (or for a web app, a worker).
Dart 代码运行在单个执行“线程”中。如果 Dart 代码在执行时阻塞,例如:处理一个需要长时间运行的计算操作或等待 I/O 完成。此时整个程序会被“冻结”。
Dart code runs in a single “thread” of execution. If Dart code blocks — for example, by performing a long-running calculation or waiting for I/O — the entire program freezes.
异步操作可以让你的程序在等待一个操作完成时继续处理其它的工作。Dart 使用 Future
对象来表示异步操作的结果。你可以用 async
和 await
关键字或 Future
类的相关 API 来配合使用 future。
Asynchronous operations let your program complete other work while waiting for an operation to finish. Dart uses Future
objects (futures) to represent the results of asynchronous operations. To work with futures, you can use either async
and await
or the Future
API.
引言
Introduction
让我们来看一些会使一个程序“冻结”的代码:
Let’s look at some code that might cause a program to freeze:
// Synchronous code // 同步执行的代码 void printDailyNewsDigest() { // var newsDigest = gatherNewsReports(); // Can take a while. var newsDigest = gatherNewsReports(); // 执行该函数会耗费一定时间。 print(newsDigest); } main() { printDailyNewsDigest(); printWinningLotteryNumbers(); printWeatherForecast(); printBaseballScore(); }
上述的程序中我们搜集当天的新闻以及用户感兴趣的其它一些信息并打印输出到控制台:
Our program gathers the news of the day, prints it, and then prints a bunch of other items of interest to the user:
<gathered news goes here> <搜集的新闻信息在这(省略……)> Winning lotto numbers: [23, 63, 87, 26, 2] 乐透中奖号码:[23、63、87、26、2] Tomorrow's forecast: 70F, sunny. 明天天气预报:21摄氏度,晴。 Baseball score: Red Sox 10, Yankees 0 棒球赛比分:红袜队 10,洋基队 0
上述的代码的问题在于:由于 gatherNewsReports()
函数阻塞,不管要等多久,剩下的代码都会在 gatherNewsReports()
函数返回了文件的内容后才会运行。如果读取文件需要耗费很长的时间,即便用户想马上知道他们是否中了乐透?明天天气如何?以及今天谁赢了比赛?都不得不等待 gatherNewsReports()
函数执行完毕。
Our code is problematic: since gatherNewsReports()
blocks, the remaining code runs only after gatherNewsReports()
returns with the contents of the file, however long that takes. If reading the file takes a long time, the user has to wait, wondering if they won the lottery, what tomorrow’s weather will be, and who won today’s game.
为了帮助保持应用的响应速度,Dart 库的创作者们在定义可能需要执行耗时操作的函数时使用一种异步模型。这类函数使用一个 future 对象返回它们的值。
To help keep the application responsive, Dart library authors use an asynchronous model when defining functions that do potentially expensive work. Such functions return their value using a future.
future是什么?
What is a future?
future 是 Future<T>
类的对象,其表示一个 T
类型的异步操作结果。如果异步操作不需要结果,则 future 的类型可为 Future<void>
。当一个返回 future 对象的函数被调用时,会发生两件事:
A future is a Future<T>
object, which represents an asynchronous operation that produces a result of type T
. If the result isn’t a usable value, then the future’s type is Future<void>
. When a function that returns a future is invoked, two things happen:
-
将函数操作列入队列等待执行并返回一个未完成的
Future
对象。The function queues up work to be done and returns an uncompleted
Future
object. -
不久后当函数操作执行完成,
Future
对象变为完成并携带一个值或一个错误。Later, when the operation is finished, the
Future
object completes with a value or with an error.
当你写的代码依赖于 future 对象时,你有两种可选的实现方式:
When writing code that depends on a future, you have two options:
-
使用关键字
async
和await
Use
async
andawait
-
使用
Future
APIUse the
Future
API
关键字 async 和 await
Async and await
关键字 async
和 await
是 Dart 语言 异步支持 的一部分。它们允许你不使用 Future
的 API 编写看起来与同步代码一样的异步代码。异步函数 即在函数头中包含关键字 async
的函数。关键字 await
只能用在异步函数中。
The async
and await
keywords are part of the Dart language’s asynchrony support. They allow you to write asynchronous code that looks like synchronous code and doesn’t use the Future
API. An async function is one that has the async
keyword before its body. The await
keyword works only in async functions.
接下来的应用使用关键字 async
和 await
读取本网站上文件的内容来模拟读取新闻。点击运行按钮开始运行该应用。或者打开一个 包含该应用的 DartPad 窗口, 来运行该应用,请点击 CONSOLE 查看应用的运行结果输出。
The following app simulates reading the news by using async
and await
to read the contents of a file on this site.
Click run to start the app.
Or open a
DartPad window containing the app,
run the app, and click CONSOLE to see the app’s output.
这里需要注意,虽然函数 printDailyNewsDigest()
是第一个被调用的函数,但是新闻信息却是最后才打印输出的,即便该新闻信息只是一行模拟文本。这是因为代码在读取和打印输出该信息时是异步的。
Notice that printDailyNewsDigest()
is the first function called, but the news is the last thing to print, even though the file contains only a single line. This is because the code that reads and prints the file is running asynchronously.
在这个例子中,函数 printDailyNewsDigest()
调用函数 gatherNewsReports()
的过程是非阻塞的。调用函数 gatherNewsReports()
时会将该函数操作列入队列执行但不会阻止其它代码的执行。程序打印输出乐透号码、天气、以及棒球赛比分;当 gatherNewsReports()
函数搜集完新闻后程序也将其打印输出。即使 gatherNewsReports()
函数需要花费一点时间来完成它的工作,这也不会造成太大的影响:用户可以在每日新闻摘要打印输出前查看其它信息。
In this example, the printDailyNewsDigest()
function calls gatherNewsReports()
, which is non-blocking. Calling gatherNewsReports()
queues up the work to be done but doesn’t stop the rest of the code from executing. The program prints the lottery numbers, the forecast, and the baseball score; when gatherNewsReports()
finishes gathering news, the program prints. If gatherNewsReports()
takes a little while to complete its work, no great harm is done: the user gets to read other things before the daily news digest is printed.
注意函数的返回类型。函数 gatherNewsReports()
的返回类型是 Future<String>
,这表示其返回一个完成时包含一个字符串值的 future 对象。而函数 printDailyNewsDigest()
因为本身没有返回值,所以在变为异步函数后其返回值类型为 Future<void>
。
Note the return types. The return type of gatherNewsReports()
is Future<String>
, which means that it returns a future that completes with a string value. The printDailyNewsDigest()
function, which doesn’t return a value, has the return type Future<void>
.
下面的图示展示了代码的执行流程。每个数字对应图示下面的一个步骤。
The following diagram shows the flow of execution through the code. Each number corresponds to a step below.
-
应用开始执行。
The app begins executing.
-
main()
函数开始以同步的方式执行并调用异步函数printDailyNewsDigest()
。The
main()
function calls the async functionprintDailyNewsDigest()
, which begins executing synchronously. -
printDailyNewsDigest()
函数开始执行,并使用关键字await
调用gatherNewsReports()
函数。printDailyNewsDigest()
usesawait
to call the functiongatherNewsReports()
, which begins executing. -
函数
gatherNewsReports()
返回一个未完成的 future 对象(Future<String>
类的一个实例)。The
gatherNewsReports()
function returns an uncompleted future (an instance ofFuture<String>
). -
因为函数
printDailyNewsDigest()
是一个异步函数并且它在等待一个值(该值由第 4 步中函数gatherNewsReports()
返回的 future 对象提供),所以它暂停其函数内代码的执行并返回一个未完成的 future 对象(在此例中,该 future 对象为Future<void>
的一个实例)给它的调用者(main()
函数)。Because
printDailyNewsDigest()
is an async function and is awaiting a value, it pauses its execution and returns an uncompleted future (in this case, an instance ofFuture<void>
) to its caller (main()
). -
执行其它的打印输出函数。因为这些函数是同步的,所以每一个函数完整地执行完后才会执行下一个函数。例如乐透的中奖号码一定会在天气预报之前打印出来。
The remaining print functions execute. Because they’re synchronous, each function executes fully before moving on to the next print function. For example, the winning lottery numbers are all printed before the weather forecast is printed.
-
当
main()
函数完成执行时,异步函数将会恢复执行。首先,函数gatherNewsReports()
返回的 future 对象完成。然后函数printDailyNewsDigest()
继续执行,最后打印出新闻。When
main()
has finished executing, the asynchronous functions can resume execution. First, the future returned bygatherNewsReports()
completes. ThenprintDailyNewsDigest()
continues executing, printing the news. -
当
printDailyNewsDigest()
函数完成执行时,其刚开始返回的 future 对象也完成,并且应用退出。When the
printDailyNewsDigest()
function body finishes executing, the future that it originally returned completes, and the app exits.
请注意异步函数是立即开始执行的(同步地),其将会在下述情况之一首次出现时暂停执行并返回一个未完成的 future 对象:
Note that an async function starts executing right away (synchronously). The function suspends execution and returns an uncompleted future when it reaches the first occurrence of any of the following:
-
函数中第一个
await
表达式出现时(在该函数从await
表达式获取到未完成的 future 之后)。The function’s first
await
expression (after the function gets the uncompleted future from that expression). -
函数中任何
return
语句的出现时。Any
return
statement in the function. -
函数体的结束。
The end of the function body.
错误处理
Handling errors
如果一个 Future
在函数返回完成时有错误,你可能想要捕获该错误。异步函数中可以使用 try-catch 语句来处理错误:
If a Future
-returning function completes with an error, you probably want to capture that error. Async functions can handle errors using try-catch:
Future<void> printDailyNewsDigest() async { try { var newsDigest = await gatherNewsReports(); print(newsDigest); } catch (e) { // Handle error... // 处理代码执行错误... } }
try-catch 语句处理异步代码错误的方式与同步代码相同: catch
语句块中的代码会在 try
语句块中的代码抛出异常时执行。
The try-catch code behaves in the same way with asynchronous code as it does with synchronous code: if the code within the try
block throws an exception, the code inside the catch
clause executes.
顺序处理
Sequential processing
你可以使用多个 await
表达式来确保各个语句在执行下一个语句之前完成:
You can use multiple await
expressions to ensure that each statement completes before executing the next statement:
// Sequential processing using async and await. // 使用关键字 async 和 await 顺序处理异步函数逻辑 main() async { await expensiveA(); await expensiveB(); doSomethingWith(await expensiveC()); }
函数 expensiveB()
将会在函数 expensiveA()
执行完毕后才执行,接下来的其它类似函数也如此。
The expensiveB()
function doesn’t execute until expensiveA()
has finished, and so on.
其它资源信息
Other resources
可以阅读下面的文档获取更多关于在 Dart 中使用 future 和异步编程的信息:
Read the following documentation for more details on using futures and asynchronous programming in Dart:
-
Dart开发语言概览 中的 异步支持 部分。
Asynchrony support, a section in the language tour.
-
futures、isolates 以及 web workers 的 API 参考文档。
API reference documentation for futures,isolates, and web workers.