What's the point?
Dart code runs in a single “thread” of execution.
Code that blocks the thread of execution can make your program freeze.
Future objects (futures) represent the results of asynchronous operations — processing or I/O to be completed later.
To suspend execution until a future completes, use await in an async function (or use then()).
To catch errors, use try-catch expressions in async functions (or use catchError()).
To run code concurrently, create an isolate (or for a web app, a worker).
关键点是什么?
1.Dart代码在单线程中执行
2.代码在运行线程中阻塞的话,会使程序冻结
3.Future对象(futures)表示异步操作的结果,进程或者IO会延迟完成
4.在async函数中使用await来挂起执行,直到future完成为止(或者使用then)
5.在async函数中使用try-catch来捕获异常(或者使用catchError())
6.要想同时运行代码,就要为一个web app或者一个worker创建一个isolate
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.
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.
Dart代码运行在一个单线程.如果Dart代码阻塞了---例如,程序计算很长时间,或者等待I/O,整个程序就会冻结。
异步操作可以允许程序在等待操作完成期间可以去完成其他工作。Dart使用Future
对象(futures
)来表示异步操作的结果。要想使用futures
,你可以使用async
和await
,或者其他Future API
。
Note: All Dart code runs in the context of an isolate that owns all of the memory that the Dart code uses. While Dart code is executing, no other code in the same isolate can run.
If you want multiple parts of Dart code to run concurrently, you can run them in separate isolates. (Web apps use workers instead of isolates.) Multiple isolates run at the same time, usually each on its own CPU core. Isolates don’t share memory, and the only way they can interact is by sending messages to each other. For more information, see the documentation for isolates or web workers.
提示:所有
Dart
代码都在一个拥有Dart代码使用的所有内存的isolate
上下文中运行。Dart代码在运行的时候,在同一隔离中的其他代码不能运行。如果你希望
Dart
代码的多个部分同时运行,你可以将它们在不同的isolate
中运行(Weba pps使用workers
代替isolates
)。多个isolate
同时运行,通常是各自运行在各自的CPU上。isolate
不共享内存,它们可以交互的唯一方式就是互相发送消息。有关更多信息,请参阅isolate
或web worker
的文档。
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.
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:
我们的程序是获取并打印当天的新闻,然后打印出一些用户感兴趣的项目:
打印结果:
Winning lotto numbers: [23, 63, 87, 26, 2]
Tomorrow's forecast: 70F, sunny.
Baseball score: Red Sox 10, Yankees 0
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.
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.
我们的代码存在的问题:当gatherNewsReports()
阻塞,不论这个方法用了多长时间,剩下的代码只能在gatherNewsReports()
返回文件内容后,才会运行。如果读取文件需要很长时间,用户必须等待很长时间才能知道他们是否中了彩票,明天的天气如何,谁赢得了今天的比赛。
为了有助于应用程序的响应性,Dart
库的作者在定义执行潜在的昂贵工作的函数时使用异步模型。这些函数使用future返回它们的值。
What is a future?
A future is a Future
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
. When a function that returns a future is invoked, two things happen:
- The function queues up work to be done and returns an uncompleted
Future
object. - Later, when the operation is finished, the
Future
object completes with a value or with an error.
When writing code that depends on a future, you have two options:
- Use
async
andawait
- Use the
Future
API
什么是Future?
future是Future
对象,它表示生成类型为T的异步操作。如果结果不是可用值,那么future的类型是Future
。当函数返回的future被调用,会发生两件事:
- 函数将要完成的工作进行排队,并且返回一个未完成的Future对象
- 之后,当操作完成时,Future对象将以一个值或者一个错误来完成
当你需要完成Future的结果时,您有两个选择:
- 使用async和await
- 使用Future API
Async and 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
关键字是Dart
语言异步支持的一部分。它们允许你不使用Future API
就可以写出类似于同步代码的异步代码。async函数的主体前面有async关键字。await关键字只在异步函数中生效。
Version note: In Dart 1.x, async functions immediately suspended execution. In Dart 2, instead of immediately suspending, async functions execute synchronously until the first await or return.
版本说明:在
Dart1.x
中,异步函数立即挂起执行。在Dart 2
中,异步函数不是立即挂起,而是同步执行,直到第一个await
或return
。
/ Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
Future printDailyNewsDigest() async {
var newsDigest = await gatherNewsReports();
print(newsDigest);
}
main() {
printDailyNewsDigest();
printWinningLotteryNumbers();
printWeatherForecast();
printBaseballScore();
}
printWinningLotteryNumbers() {
print('Winning lotto numbers: [23, 63, 87, 26, 2]');
}
printWeatherForecast() {
print("Tomorrow's forecast: 70F, sunny.");
}
printBaseballScore() {
print('Baseball score: Red Sox 10, Yankees 0');
}
const news = '';
const oneSecond = Duration(seconds: 1);
// Imagine that this function is more complex and slow. :)
Future gatherNewsReports() =>
Future.delayed(oneSecond, () => news);
// Alternatively, you can get news from a server using features
// from either dart:io or dart:html. For example:
//
// import 'dart:html';
//
// Future gatherNewsReportsFromServer() => HttpRequest.getString(
// 'https://www.dartlang.org/f/dailyNewsDigest.txt',
// );
输出结果:
Winning lotto numbers: [23, 63, 87, 26, 2]
Tomorrow's forecast: 70F, sunny.
Baseball score: Red Sox 10, Yankees 0
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.
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.
Note the return types. The return type of gatherNewsReports() is Future
The following diagram shows the flow of execution through the code. Each number corresponds to a step below.
注意,printDailyNewsDigest()
是第一个被调用的函数,但是新闻是最后打印的,即使文件只有一行。这是因为读取和打印文件的代码是异步运行的。
在这个例子中,printDailyNewsDigest()
函数调用非阻塞的函数gatherNewsReports()
。调用gatherNewsReports()
将要完成的工作排队,但不会阻止其余代码的执行。当gatherNewsReports()
完成收集新闻时,这个程序打印彩票号码、预测和棒球分数。如果gatherNewsReports()
用了很少的时间来完成它的工作,那么并不会造成很大的损害。用户可以在每日新闻摘要打印之前阅读其他内容。
注意返回值类型。gatherNewsReports()
的返回值类型是Future
,意思就是它返回了一个带有字符串值的Future。printDailyNewsDigest()
函数不返回值,它的返回类型是 Future
下图展示了一个代码的执行流程,每个数字对应下面的一个步骤。
- The app begins executing.
- The main() function calls the async function printDailyNewsDigest(), which begins executing synchronously.
- printDailyNewsDigest() uses await to call the function gatherNewsReports(), which begins executing.
- The gatherNewsReports() function returns an uncompleted future (an instance of Future
). - 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 of Future
) 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.
- When main() has finished executing, the asynchronous functions can resume execution. First, the future returned by gatherNewsReports() completes. Then printDailyNewsDigest() continues executing, printing the news.
- When the printDailyNewsDigest() function body finishes executing, the future that it originally returned completes, and the app exits.
- app开始执行。
- 祝函数调用异步函数
printDailyNewsDigest()
,该函数开始同步执行 -
printDailyNewsDigest()
使用wait来调用开始执行的函数gatherNewsReports()
-
gatherNewsReports()
函数返回一个未完成的future(一个Future实例) - 因为
printDailyNewsDigest()
是一个异步函数,并且正在等待一个值,它暂停执行并返回一个未完成的future(在本例中是Future实例)给调用者(主函数main())。 - 执行剩余的打印函数。因为它们是同步的,所以每个函数在转移到下一个打印函数之前都要执行完全。例如,中奖彩票号码在天气预报打印之前都要打印出来。
- 当main()函数执行后,异步函数可以恢复执行。首先
gatherNewsReports()
函数完成后返回future。然后printDailyNewsDigest()
函数继续执行,打印新闻。 - 当
printDailyNewsDigest()
函数体结束执行时,最初返回的future完成,app退出。
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:
- The function’s first
await
expression (after the function gets the uncompleted future from that expression). - Any
return
statement in the function. - The end of the function body.
注意,异步函数立即开始执行(同步地)。函数在遇到以下情况时暂停执行并返回一个未完成的future:
- 函数的第一个
await
表达式(函数在从该表达式中获得未完成的future之后) - 函数中任何return语句
- 函数体的结尾
Handling errors(处理错误)
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-returning函数完成时有错误,你可能希望捕获这个错误。异步函数可以使用try-catch处理错误:
Future printDailyNewsDigest() async {
try {
var newsDigest = await gatherNewsReports();
print(newsDigest);
} catch (e) {
// Handle error...
}
}
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.
try-catch代码在异步代码的行为方式与在同步代码的行为方式相同:如果try
代码块中的代码抛出异常,catch
中的代码将执行。
Sequential processing(顺序处理)
You can use multiple await expressions to ensure that each statement completes before executing the next statement:
你可以使用多个await
表达式来确保每个语句在执行下一个语句之前完成:
// Sequential processing using async and await.
main() async {
await expensiveA();
await expensiveB();
doSomethingWith(await expensiveC());
}
The expensiveB() function doesn’t execute until expensiveA() has finished, and so on.
expensiveB()
函数在expensiveA()
函数结束之前不会执行,以此类推。
The Future API
Before async
and await
were added in Dart 1.9, you had to use the Future
API. You might still see the Future
API used in older code and in code that needs more functionality than async-await offers.
To write asynchronous code using the Future
API, you use the then()
method to register a callback. This callback fires when the Future
completes.
The following app simulates reading the news by using the Future
API to read the contents of a file on this site. Open a DartPad window containing the app, run the app, and click CONSOLE to see the app’s output.
在Dart1.9中添加async
和await
之前,你必须要使用Future API
,你可能仍然会在老代码中或者那些需要比async-await
提供的功能更多的代码中看到Future API
还在被使用。
要使用Future API
编写异步代码,可以使用then()
方法来注册回调。Future
完成后将触发回调。
下面的应用程序通过使用Future API
来读取网站上文件的内容来模拟阅读新闻。
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
Future printDailyNewsDigest() {
final future = gatherNewsReports();
return future.then(print);
// You don't *have* to return the future here.
// But if you don't, callers can't await it.
}
main() {
printDailyNewsDigest();
printWinningLotteryNumbers();
printWeatherForecast();
printBaseballScore();
}
printWinningLotteryNumbers() {
print('Winning lotto numbers: [23, 63, 87, 26, 2]');
}
printWeatherForecast() {
print("Tomorrow's forecast: 70F, sunny.");
}
printBaseballScore() {
print('Baseball score: Red Sox 10, Yankees 0');
}
const news = '';
const oneSecond = Duration(seconds: 1);
// Imagine that this function is more complex and slow. :)
Future gatherNewsReports() =>
Future.delayed(oneSecond, () => news);
// Alternatively, you can get news from a server using features
// from either dart:io or dart:html. For example:
//
// import 'dart:html';
//
// Future gatherNewsReportsFromServer() => HttpRequest.getString(
// 'https://www.dartlang.org/f/dailyNewsDigest.txt',
// );
结果:
Winning lotto numbers: [23, 63, 87, 26, 2]
Tomorrow's forecast: 70F, sunny.
Baseball score: Red Sox 10, Yankees 0
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 the file is running asynchronously.
This app executes as follows:
- The app begins executing.
- The main function calls the
printDailyNewsDigest()
function, which does not return immediately, but callsgatherNewsReports()
. -
gatherNewsReports()
starts gathering news and returns a Future. -
printDailyNewsDigest()
usesthen()
to specify a response to the Future. Callingthen()
returns a new Future that will complete with the value returned bythen()’s
callback. - 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.
- When all of the news has arrived, the Future returned by
gatherNewsReports()
completes with a string containing the gathered news. - The code specified by
then()
inprintDailyNewsDigest()
runs, printing the news. - The app exits.
注意,printDailyNewsDigest()
是第一个被调用的函数,但是新闻是最后要最后要打印的内容,即使文件只包含一行。这是因为读取文件的代码是异步运行的。
这个app执行如下:
- app开始zhixing
- 主函数调用
printDailyNewsDigest()
函数,该函数不会立即返回,二十调用gatherNewsReports()
函数。 -
gatherNewsReports()
函数开始收集新闻,并返回一个Future对象 -
printDailyNewsDigest()
函数使用then()
来指定对Future对象的响应。调用then
来返回一个新的Future对象,该Future将以then()的回调返回的值来完成。 - 执行其余打印函数。因为它们是同步的,每一个函数都会在执行下一个打印函数之前全部执行完毕。例如,彩票中奖号码在天气预报打印之前打印出来。
- 当所有的新闻收到以后,
gatherNewsReports()
函数在结束时会返回一个带有新闻信息的字符串值的Future。 -
printDailyNewsDigest()
函数中then()
指定的代码运行来打印新闻。 - app退出
Note: In the
printDailyNewsDigest()
function, the codefuture.then(print)
is equivalent to the following:
future.then((newsDigest) => print(newsDigest))
注意:在
printDailyNewsDigest()
中,代码future.then(print)
等价于:future.then((newsDigest) => print(newsDigest))
Alternatively, the code inside then()
can use curly braces:
或者,then()
中的代码可以使用花括号:
Future printDailyNewsDigest() {
final future = gatherNewsReports();
return future.then((newsDigest) {
print(newsDigest);
// Do something else...
});
}
You need to provide an argument to then()
’s callback, even if the Future
is of type Future
. By convention, an unused argument is named _
(underscore).
你需要为then()
的回调提供一个参数,即使Future
是Future
类型。按照惯例,未使用的参数命名为_
(下划线)
final future = printDailyNewsDigest();
return future.then((_) {
// Code that doesn't use the `_` parameter...
print('All reports printed.');
});
Handling errors
With the Future API
, you can capture an error usingcatchError()
:
在Future API
中,你可以使用catchError()
来捕获错误:
Future printDailyNewsDigest() =>
gatherNewsReports().then(print).catchError(handleError);
If the news stream isn’t available for reading, the code above executes as follows:
- The future returned by
gatherNewsReports()
completes with an error. - The future returned by
then()
completes with an error;print()
isn’t called. - The callback for
catchError()
(handleError()
) handles the error, the future returned bycatchError()
completes normally, and the error does not propagate.
如果新闻流不可读,上面的代码执行如下:
-
gatherNewsReports()
函数返回的future
对象将以错误结束
2.then()
返回的future
以错误结束;print()
不会被调用。 -
catchError()
(handleError()
)的回调函数处理错误,catchError()
返回future
正常完成,错误不会传播。
Chaining
catchError()
tothen()
is a common pattern when using theFuture API
. Consider this pairing theFuture API
’s equivalent of try-catch blocks.
将
catchError()
链接到then()
是使用未来API时的一种常见模式。考虑将Future API
的try-catch块配对。
Like then()
, catchError()
returns a new Future
that completes with the return value of its callback.
For more details and examples, read Futures and Error Handling.
与then()
类似,catchError()
返回一个新的Future
,该Future
使用回调的返回值完成。
更多细节和例子,请参考。。。。
Calling multiple functions that return futures
Consider three functions, expensiveA(), expensiveB(), and expensiveC(), that return Future objects. You can invoke them sequentially (one function starts when a previous one completes), or you can kick off all of them at the same time and do something once all the values return. The Future interface is fluid enough to deal with both use cases.
思考一下三个可以返回Future
对象的函数,expensiveA()
, expensiveB()
和 expensiveC()
。你可以按顺序调用它们(一个函数在前一个函数完成时启动),或者你可以同时启动所有函数,并在所有值返回时执行这些操作。Future接口是足够灵活,可以处理这两个用例。
Chaining function calls using then()使用then()链函数调用
When Future-returning functions need to run in order, use chained then()
calls:
当Future-returning
函数需要按顺序运行,使用then()
链调用:
expensiveA()
.then((aValue) => expensiveB())
.then((bValue) => expensiveC())
.then((cValue) => doSomethingWith(cValue));
Nested callbacks also work, but they’re harder to read and not as Dart-y.
嵌套回调也可以运行,但是它们很难读懂,也不想Dart-y
那样容易。
Waiting on multiple futures to complete using Future.wait()
If the order of execution of the functions is not important, you can use Future.wait().
When you pass Future.wait() a list of futures, it immediately returns a Future. That future doesn’t complete until all of the given futures have completed. Then it completes with a list containing the values produced by each future in the original list.
如果函数的执行顺序不重要,你可以用Future.wait()
。
当你传给Future.wait()
一个futures
列表时,她立即返回一个Future
。直到所有给定的futures
都完成了,这个future
才算完成。然后用一个包含原始列表中每个future
生成的值的列表来完成。
Future.wait([expensiveA(), expensiveB(), expensiveC()])
.then((List responses) => chooseBestResponse(responses, moreInfo))
.catchError(handleError);
If any of the invoked functions completes with an error, the Future
returned by Future.wait()
also completes with an error. Use catchError()
to handle the error.
如果任何被调用的函数都以错误来结束,那么Future.wait()
返回的Future
也以错误完成。使用catchError()
来处理错误。
Other resources
Read the following documentation for more details on using futures and asynchronous programming in Dart:
- Futures and Error Handling, an article that starts where this tutorial ends.
- The Event Loop and Dart, an article that describes how to schedule tasks using futures.
- Asynchrony support, a section in the language tour.
- API reference documentation for futures, isolates, and web workers.
有关在Dart
中使用future
和异步编程的更多细节,请阅读以下文档:
- Futures 和 Error Handling,本教程结束的地方开始的一片文章
- Event Loop 和 Dart,
这篇文章描述了如何使用future来安排任务。 - 异步支持,语言之旅的一部分
- futures, isolates, and web workers.API参考文档。
What next?
- The next tutorial, Asynchronous Programming: Streams, shows you how to work with an event stream.
下一个教程,异步编程: Streams给你展示如何处理事件流。