非同步程式設計:Future、async、await
本教學課程教導您如何使用 Future 以及 async
和 await
關鍵字來撰寫非同步程式碼。透過內嵌的 DartPad 編輯器,您可以執行範例程式碼和完成練習來測試您的知識。
為了充分利用本教學課程,您應該具備以下條件
- 具備 Dart 基礎語法知識。
- 具備在其他語言中撰寫非同步程式碼的經驗。
- 已啟用
discarded_futures
和unawaited_futures
lints。
本教學課程涵蓋以下內容
- 如何以及何時使用
async
和await
關鍵字。 - 使用
async
和await
如何影響執行順序。 - 如何在
async
函式中使用try-catch
運算式來處理來自非同步呼叫的錯誤。
完成本教學課程的預計時間:40-60 分鐘。
本教學課程中的練習包含部分完成的程式碼片段。您可以使用 DartPad 完成程式碼並點擊「執行」按鈕來測試您的知識。請勿編輯 main
函式或以下的測試程式碼。
如果您需要協助,請在每個練習後展開「提示」或「解答」下拉選單。
為何非同步程式碼很重要
#非同步操作可讓您的程式在等待另一個操作完成時繼續執行工作。以下是一些常見的非同步操作
- 透過網路擷取資料。
- 寫入資料庫。
- 從檔案讀取資料。
此類非同步計算通常會將其結果以 Future
或 Stream
(如果結果有多個部分)的形式提供。這些計算會將非同步性引入程式中。為了適應最初的非同步性,其他純 Dart 函式也需要變成非同步函式。
若要與這些非同步結果互動,您可以使用 async
和 await
關鍵字。大多數非同步函式只是 async Dart 函式,它們可能在深層次上依賴於固有的非同步計算。
範例:不正確地使用非同步函式
#以下範例示範了錯誤使用非同步函式 (fetchUserOrder()
) 的方式。稍後您將使用 async
和 await
修正此範例。在執行此範例之前,請嘗試找出問題所在 — 您認為輸出會是什麼?
// This example shows how *not* to write asynchronous Dart code.
String createOrderMessage() {
var order = fetchUserOrder();
return 'Your order is: $order';
}
Future<String> fetchUserOrder() =>
// Imagine that this function is more complex and slow.
Future.delayed(const Duration(seconds: 2), () => 'Large Latte');
void main() {
print(createOrderMessage());
}
以下說明為何此範例無法印出 fetchUserOrder()
最終產生的值
fetchUserOrder()
是一個非同步函式,它在延遲後會提供一個描述使用者訂單的字串:「大杯拿鐵」。- 為了取得使用者的訂單,
createOrderMessage()
應該呼叫fetchUserOrder()
並等待它完成。由於createOrderMessage()
*沒有* 等待fetchUserOrder()
完成,因此createOrderMessage()
無法取得fetchUserOrder()
最終提供的字串值。 - 相反地,
createOrderMessage()
取得的是待完成工作的表示:一個未完成的 Future。您將在下一節中瞭解更多關於 Future 的資訊。 - 由於
createOrderMessage()
無法取得描述使用者訂單的值,因此範例無法在主控台中印出「Large Latte」,而是印出「Your order is: Instance of '_Future<String>'。」
在接下來的章節中,您將學習關於 Future 以及如何使用 Future(使用 async
和 await
),以便您能夠撰寫必要的程式碼,使 fetchUserOrder()
在主控台中印出所需的值(「Large Latte」)。
什麼是 Future?
#future(小寫 "f")是 Future(大寫 "F")類別的實例。future 代表非同步操作的結果,並且可以有兩種狀態:未完成或已完成。
未完成
#當您呼叫非同步函式時,它會傳回一個未完成的 future。該 future 正在等待函式的非同步操作完成或擲回錯誤。
已完成
#如果非同步操作成功,future 會以值完成。否則,它會以錯誤完成。
以值完成
#類型為 Future<T>
的 future 會以類型為 T
的值完成。例如,類型為 Future<String>
的 future 會產生字串值。如果 future 沒有產生可用的值,則 future 的類型為 Future<void>
。
以錯誤完成
#如果函式執行的非同步操作因任何原因失敗,future 會以錯誤完成。
範例:Future 簡介
#在以下範例中,fetchUserOrder()
傳回一個在印出到主控台後完成的 future。由於它沒有傳回可用的值,因此 fetchUserOrder()
的類型為 Future<void>
。在您執行範例之前,請嘗試預測哪個會先印出:「Large Latte」還是「Fetching user order...」。
Future<void> fetchUserOrder() {
// Imagine that this function is fetching user info from another service or database.
return Future.delayed(const Duration(seconds: 2), () => print('Large Latte'));
}
void main() {
fetchUserOrder();
print('Fetching user order...');
}
在前面的範例中,即使 fetchUserOrder()
在第 8 行的 print()
呼叫之前執行,主控台也會先顯示第 8 行(「Fetching user order...」)的輸出,然後才顯示 fetchUserOrder()
(「Large Latte」)的輸出。這是因為 fetchUserOrder()
在印出「Large Latte」之前會延遲。
範例:以錯誤完成
#執行以下範例以查看 future 如何以錯誤完成。稍後您將學習如何處理錯誤。
Future<void> fetchUserOrder() {
// Imagine that this function is fetching user info but encounters a bug.
return Future.delayed(
const Duration(seconds: 2),
() => throw Exception('Logout failed: user ID is invalid'),
);
}
void main() {
fetchUserOrder();
print('Fetching user order...');
}
在此範例中,fetchUserOrder()
以錯誤完成,指出使用者 ID 無效。
您已經了解了 future 以及它們如何完成,但是您如何使用非同步函式的結果呢?在下一節中,您將學習如何使用 async
和 await
關鍵字取得結果。
使用 Future:async 與 await
#async
和 await
關鍵字提供了一種宣告式方法來定義非同步函式並使用其結果。使用 async
和 await
時,請記住以下兩個基本準則
- 若要定義 async 函式,請在函式主體之前新增
async
await
關鍵字僅在async
函式中運作。
以下範例將 main()
從同步函式轉換為非同步函式。
首先,在函式主體之前新增 async
關鍵字
void main() async { ··· }
如果函式具有宣告的傳回類型,則將類型更新為 Future<T>
,其中 T
是函式傳回值的類型。如果函式未明確傳回值,則傳回類型為 Future<void>
Future<void> main() async { ··· }
現在您有了 async
函式,您可以使用 await
關鍵字等待 future 完成
print(await createOrderMessage());
如下列兩個範例所示,async
和 await
關鍵字會產生看起來很像同步程式碼的非同步程式碼。唯一的差異在非同步範例中以醒目提示,如果您的視窗夠寬,則非同步範例位於同步範例的右側。
範例:同步函式
#String createOrderMessage() {
var order = fetchUserOrder();
return 'Your order is: $order';
}
Future<String> fetchUserOrder() =>
// Imagine that this function is
// more complex and slow.
Future.delayed(const Duration(seconds: 2), () => 'Large Latte');
void main() {
print('Fetching user order...');
print(createOrderMessage());
}
Fetching user order...
Your order is: Instance of 'Future<String>'
如下列兩個範例所示,它的運作方式就像同步程式碼。
範例:非同步函式
#Future<String> createOrderMessage() async {
var order = await fetchUserOrder();
return 'Your order is: $order';
}
Future<String> fetchUserOrder() =>
// Imagine that this function is
// more complex and slow.
Future.delayed(const Duration(seconds: 2), () => 'Large Latte');
Future<void> main() async {
print('Fetching user order...');
print(await createOrderMessage());
}
Fetching user order...
Your order is: Large Latte
非同步範例在三個方面有所不同
createOrderMessage()
的傳回類型從String
變更為Future<String>
。async
關鍵字出現在createOrderMessage()
和main()
的函式主體之前。await
關鍵字出現在呼叫非同步函式fetchUserOrder()
和createOrderMessage()
之前。
async 與 await 的執行流程
#async
函式會同步執行,直到遇到第一個 await
關鍵字。這表示在 async
函式主體內,第一個 await
關鍵字之前的所有同步程式碼都會立即執行。
範例:async 函式內的執行
#執行以下範例以查看執行如何在 async
函式主體內進行。您認為輸出會是什麼?
Future<void> printOrderMessage() async {
print('Awaiting user order...');
var order = await fetchUserOrder();
print('Your order is: $order');
}
Future<String> fetchUserOrder() {
// Imagine that this function is more complex and slow.
return Future.delayed(const Duration(seconds: 4), () => 'Large Latte');
}
void main() async {
countSeconds(4);
await printOrderMessage();
}
// You can ignore this function - it's here to visualize delay time in this example.
void countSeconds(int s) {
for (var i = 1; i <= s; i++) {
Future.delayed(Duration(seconds: i), () => print(i));
}
}
在執行前面範例中的程式碼後,嘗試反轉第 2 行和第 3 行
var order = await fetchUserOrder();
print('Awaiting user order...');
請注意,輸出的時序會移動,現在 print('Awaiting user order')
出現在 printOrderMessage()
中第一個 await
關鍵字之後。
練習:練習使用 async 與 await
#以下練習是一個失敗的單元測試,其中包含部分完成的程式碼片段。您的任務是完成練習,撰寫程式碼以使測試通過。您不需要實作 main()
。
為了模擬非同步操作,請呼叫以下為您提供的函式
函式 | 類型簽名 | 描述 |
---|---|---|
fetchRole() | Future<String> fetchRole() | 取得使用者角色的簡短描述。 |
fetchLoginAmount() | Future<int> fetchLoginAmount() | 取得使用者登入的次數。 |
第 1 部分:reportUserRole()
#將程式碼新增至 reportUserRole()
函式,使其執行以下操作
- 傳回一個 future,該 future 以以下字串完成:「User role: <user role>」
- 注意:您必須使用
fetchRole()
傳回的實際值;複製並貼上範例傳回值不會使測試通過。 - 範例傳回值:「User role: tester」
- 注意:您必須使用
- 透過呼叫提供的函式
fetchRole()
取得使用者角色。
第 2 部分:reportLogins()
#實作一個 async
函式 reportLogins()
,使其執行以下操作
- 傳回字串「Total number of logins: <# of logins>」。
- 注意:您必須使用
fetchLoginAmount()
傳回的實際值;複製並貼上範例傳回值不會使測試通過。 reportLogins()
的範例傳回值:「Total number of logins: 57」
- 注意:您必須使用
- 透過呼叫提供的函式
fetchLoginAmount()
取得登入次數。
// Part 1
// Call the provided async function fetchRole()
// to return the user role.
Future<String> reportUserRole() async {
// TODO: Implement the reportUserRole function here.
}
// Part 2
// TODO: Implement the reportLogins function here.
// Call the provided async function fetchLoginAmount()
// to return the number of times that the user has logged in.
reportLogins() {}
// The following functions those provided to you to simulate
// asynchronous operations that could take a while.
Future<String> fetchRole() => Future.delayed(_halfSecond, () => _role);
Future<int> fetchLoginAmount() => Future.delayed(_halfSecond, () => _logins);
// The following code is used to test and provide feedback on your solution.
// There is no need to read or modify it.
void main() async {
print('Testing...');
List<String> messages = [];
const passed = 'PASSED';
const testFailedMessage = 'Test failed for the function:';
const typoMessage = 'Test failed! Check for typos in your return value';
try {
messages
..add(_makeReadable(
testLabel: 'Part 1',
testResult: await _asyncEquals(
expected: 'User role: administrator',
actual: await reportUserRole(),
typoKeyword: _role,
),
readableErrors: {
typoMessage: typoMessage,
'null':
'Test failed! Did you forget to implement or return from reportUserRole?',
'User role: Instance of \'Future<String>\'':
'$testFailedMessage reportUserRole. Did you use the await keyword?',
'User role: Instance of \'_Future<String>\'':
'$testFailedMessage reportUserRole. Did you use the await keyword?',
'User role:':
'$testFailedMessage reportUserRole. Did you return a user role?',
'User role: ':
'$testFailedMessage reportUserRole. Did you return a user role?',
'User role: tester':
'$testFailedMessage reportUserRole. Did you invoke fetchRole to fetch the user\'s role?',
}))
..add(_makeReadable(
testLabel: 'Part 2',
testResult: await _asyncEquals(
expected: 'Total number of logins: 42',
actual: await reportLogins(),
typoKeyword: _logins.toString(),
),
readableErrors: {
typoMessage: typoMessage,
'null':
'Test failed! Did you forget to implement or return from reportLogins?',
'Total number of logins: Instance of \'Future<int>\'':
'$testFailedMessage reportLogins. Did you use the await keyword?',
'Total number of logins: Instance of \'_Future<int>\'':
'$testFailedMessage reportLogins. Did you use the await keyword?',
'Total number of logins: ':
'$testFailedMessage reportLogins. Did you return the number of logins?',
'Total number of logins:':
'$testFailedMessage reportLogins. Did you return the number of logins?',
'Total number of logins: 57':
'$testFailedMessage reportLogins. Did you invoke fetchLoginAmount to fetch the number of user logins?',
}))
..removeWhere((m) => m.contains(passed))
..toList();
if (messages.isEmpty) {
print('Success. All tests passed!');
} else {
messages.forEach(print);
}
} on UnimplementedError {
print(
'Test failed! Did you forget to implement or return from reportUserRole?');
} catch (e) {
print('Tried to run solution, but received an exception: $e');
}
}
const _role = 'administrator';
const _logins = 42;
const _halfSecond = Duration(milliseconds: 500);
// Test helpers.
String _makeReadable({
required String testResult,
required Map<String, String> readableErrors,
required String testLabel,
}) {
if (readableErrors.containsKey(testResult)) {
var readable = readableErrors[testResult];
return '$testLabel $readable';
} else {
return '$testLabel $testResult';
}
}
// Assertions used in tests.
Future<String> _asyncEquals({
required String expected,
required dynamic actual,
required String typoKeyword,
}) async {
var strActual = actual is String ? actual : actual.toString();
try {
if (expected == actual) {
return 'PASSED';
} else if (strActual.contains(typoKeyword)) {
return 'Test failed! Check for typos in your return value';
} else {
return strActual;
}
} catch (e) {
return e.toString();
}
}
提示
您是否記得將 async
關鍵字新增至 reportUserRole
函式?
您是否記得在叫用 fetchRole()
之前使用 await
關鍵字?
請記住:reportUserRole
需要傳回 Future
。
解答
Future<String> reportUserRole() async {
final username = await fetchRole();
return 'User role: $username';
}
Future<String> reportLogins() async {
final logins = await fetchLoginAmount();
return 'Total number of logins: $logins';
}
處理錯誤
#若要在 async
函式中處理錯誤,請使用 try-catch
try {
print('Awaiting user order...');
var order = await fetchUserOrder();
} catch (err) {
print('Caught error: $err');
}
在 async
函式中,您可以像在同步程式碼中一樣撰寫 try-catch 子句。
範例:搭配 try-catch 的 async 與 await
#執行以下範例以查看如何處理來自非同步函式的錯誤。您認為輸出會是什麼?
Future<void> printOrderMessage() async {
try {
print('Awaiting user order...');
var order = await fetchUserOrder();
print(order);
} catch (err) {
print('Caught error: $err');
}
}
Future<String> fetchUserOrder() {
// Imagine that this function is more complex.
var str = Future.delayed(
const Duration(seconds: 4),
() => throw 'Cannot locate user order',
);
return str;
}
void main() async {
await printOrderMessage();
}
練習:練習處理錯誤
#以下練習提供使用非同步程式碼處理錯誤的練習,使用上一節中描述的方法。為了模擬非同步操作,您的程式碼將呼叫以下為您提供的函式
函式 | 類型簽名 | 描述 |
---|---|---|
fetchNewUsername() | Future<String> fetchNewUsername() | 傳回您可以用來取代舊使用者名稱的新使用者名稱。 |
使用 async
和 await
實作一個非同步 changeUsername()
函式,使其執行以下操作
- 呼叫提供的非同步函式
fetchNewUsername()
並傳回其結果。changeUsername()
的範例傳回值:「jane_smith_92」
- 捕捉發生的任何錯誤並傳回錯誤的字串值。
- 您可以使用 toString() 方法將 Exceptions 和 Errors 字串化。
// TODO: Implement changeUsername here.
changeUsername() {}
// The following function is provided to you to simulate
// an asynchronous operation that could take a while and
// potentially throw an exception.
Future<String> fetchNewUsername() =>
Future.delayed(const Duration(milliseconds: 500), () => throw UserError());
class UserError implements Exception {
@override
String toString() => 'New username is invalid';
}
// The following code is used to test and provide feedback on your solution.
// There is no need to read or modify it.
void main() async {
final List<String> messages = [];
const typoMessage = 'Test failed! Check for typos in your return value';
print('Testing...');
try {
messages
..add(_makeReadable(
testLabel: '',
testResult: await _asyncDidCatchException(changeUsername),
readableErrors: {
typoMessage: typoMessage,
_noCatch:
'Did you remember to call fetchNewUsername within a try/catch block?',
}))
..add(_makeReadable(
testLabel: '',
testResult: await _asyncErrorEquals(changeUsername),
readableErrors: {
typoMessage: typoMessage,
_noCatch:
'Did you remember to call fetchNewUsername within a try/catch block?',
}))
..removeWhere((m) => m.contains(_passed))
..toList();
if (messages.isEmpty) {
print('Success. All tests passed!');
} else {
messages.forEach(print);
}
} catch (e) {
print('Tried to run solution, but received an exception: $e');
}
}
// Test helpers.
String _makeReadable({
required String testResult,
required Map<String, String> readableErrors,
required String testLabel,
}) {
if (readableErrors.containsKey(testResult)) {
final readable = readableErrors[testResult];
return '$testLabel $readable';
} else {
return '$testLabel $testResult';
}
}
Future<String> _asyncErrorEquals(Function fn) async {
final result = await fn();
if (result == UserError().toString()) {
return _passed;
} else {
return 'Test failed! Did you stringify and return the caught error?';
}
}
Future<String> _asyncDidCatchException(Function fn) async {
var caught = true;
try {
await fn();
} on UserError catch (_) {
caught = false;
}
if (caught == false) {
return _noCatch;
} else {
return _passed;
}
}
const _passed = 'PASSED';
const _noCatch = 'NO_CATCH';
提示
實作 changeUsername
以傳回來自 fetchNewUsername
的字串,或者,如果失敗,則傳回發生的任何錯誤的字串值。
請記住:您可以使用 try-catch 陳述式來捕捉和處理錯誤。
解答
Future<String> changeUsername() async {
try {
return await fetchNewUsername();
} catch (err) {
return err.toString();
}
}
練習:整合練習
#現在是時候練習您在最後一個練習中學到的內容了。為了模擬非同步操作,本練習提供了非同步函式 fetchUsername()
和 logoutUser()
函式 | 類型簽名 | 描述 |
---|---|---|
fetchUsername() | Future<String> fetchUsername() | 傳回與目前使用者相關聯的名稱。 |
logoutUser() | Future<String> logoutUser() | 執行目前使用者的登出,並傳回已登出的使用者名稱。 |
撰寫以下內容
第 1 部分:addHello()
#- 撰寫一個函式
addHello()
,該函式接受單一String
引數。 addHello()
傳回其String
引數,並在其前面加上'Hello '
。
範例:addHello('Jon')
傳回'Hello Jon'
。
第 2 部分:greetUser()
#- 撰寫一個函式
greetUser()
,該函式不接受引數。 - 為了取得使用者名稱,
greetUser()
呼叫提供的非同步函式fetchUsername()
。 greetUser()
透過呼叫addHello()
、將使用者名稱傳遞給它並傳回結果,為使用者建立問候語。
範例:如果fetchUsername()
傳回 'Jenny',則greetUser()
傳回 'Hello Jenny'。
第 3 部分:sayGoodbye()
#- 撰寫一個函式
sayGoodbye()
,使其執行以下操作- 不接受引數。
- 捕捉任何錯誤。
- 呼叫提供的非同步函式
logoutUser()
。
- 如果
logoutUser()
失敗,sayGoodbye()
會傳回您喜歡的任何字串。 - 如果
logoutUser()
成功,sayGoodbye()
會傳回字串「<result> Thanks, see you next time」,其中 <result> 是呼叫logoutUser()
傳回的字串值。
// Part 1
addHello(String user) {}
// Part 2
// Call the provided async function fetchUsername()
// to return the username.
greetUser() {}
// Part 3
// Call the provided async function logoutUser()
// to log out the user.
sayGoodbye() {}
// The following functions are provided to you to use in your solutions.
Future<String> fetchUsername() => Future.delayed(_halfSecond, () => 'Jean');
Future<String> logoutUser() => Future.delayed(_halfSecond, _failOnce);
// The following code is used to test and provide feedback on your solution.
// There is no need to read or modify it.
void main() async {
const didNotImplement =
'Test failed! Did you forget to implement or return from';
final List<String> messages = [];
print('Testing...');
try {
messages
..add(_makeReadable(
testLabel: 'Part 1',
testResult: await _asyncEquals(
expected: 'Hello Jerry',
actual: addHello('Jerry'),
typoKeyword: 'Jerry'),
readableErrors: {
_typoMessage: _typoMessage,
'null': '$didNotImplement addHello?',
'Hello Instance of \'Future<String>\'':
'Looks like you forgot to use the \'await\' keyword!',
'Hello Instance of \'_Future<String>\'':
'Looks like you forgot to use the \'await\' keyword!',
}))
..add(_makeReadable(
testLabel: 'Part 2',
testResult: await _asyncEquals(
expected: 'Hello Jean',
actual: await greetUser(),
typoKeyword: 'Jean'),
readableErrors: {
_typoMessage: _typoMessage,
'null': '$didNotImplement greetUser?',
'HelloJean':
'Looks like you forgot the space between \'Hello\' and \'Jean\'',
'Hello Instance of \'Future<String>\'':
'Looks like you forgot to use the \'await\' keyword!',
'Hello Instance of \'_Future<String>\'':
'Looks like you forgot to use the \'await\' keyword!',
'{Closure: (String) => dynamic from Function \'addHello\': static.(await fetchUsername())}':
'Did you place the \'\$\' character correctly?',
'{Closure \'addHello\'(await fetchUsername())}':
'Did you place the \'\$\' character correctly?',
}))
..add(_makeReadable(
testLabel: 'Part 3',
testResult: await _asyncDidCatchException(sayGoodbye),
readableErrors: {
_typoMessage:
'$_typoMessage. Did you add the text \'Thanks, see you next time\'?',
'null': '$didNotImplement sayGoodbye?',
_noCatch:
'Did you remember to call logoutUser within a try/catch block?',
'Instance of \'Future<String>\' Thanks, see you next time':
'Did you remember to use the \'await\' keyword in the sayGoodbye function?',
'Instance of \'_Future<String>\' Thanks, see you next time':
'Did you remember to use the \'await\' keyword in the sayGoodbye function?',
}))
..add(_makeReadable(
testLabel: 'Part 3',
testResult: await _asyncEquals(
expected: 'Success! Thanks, see you next time',
actual: await sayGoodbye(),
typoKeyword: 'Success'),
readableErrors: {
_typoMessage:
'$_typoMessage. Did you add the text \'Thanks, see you next time\'?',
'null': '$didNotImplement sayGoodbye?',
_noCatch:
'Did you remember to call logoutUser within a try/catch block?',
'Instance of \'Future<String>\' Thanks, see you next time':
'Did you remember to use the \'await\' keyword in the sayGoodbye function?',
'Instance of \'_Future<String>\' Thanks, see you next time':
'Did you remember to use the \'await\' keyword in the sayGoodbye function?',
'Instance of \'_Exception\'':
'CAUGHT Did you remember to return a string?',
}))
..removeWhere((m) => m.contains(_passed))
..toList();
if (messages.isEmpty) {
print('Success. All tests passed!');
} else {
messages.forEach(print);
}
} catch (e) {
print('Tried to run solution, but received an exception: $e');
}
}
// Test helpers.
String _makeReadable({
required String testResult,
required Map<String, String> readableErrors,
required String testLabel,
}) {
String? readable;
if (readableErrors.containsKey(testResult)) {
readable = readableErrors[testResult];
return '$testLabel $readable';
} else if ((testResult != _passed) && (testResult.length < 18)) {
readable = _typoMessage;
return '$testLabel $readable';
} else {
return '$testLabel $testResult';
}
}
Future<String> _asyncEquals({
required String expected,
required dynamic actual,
required String typoKeyword,
}) async {
final strActual = actual is String ? actual : actual.toString();
try {
if (expected == actual) {
return _passed;
} else if (strActual.contains(typoKeyword)) {
return _typoMessage;
} else {
return strActual;
}
} catch (e) {
return e.toString();
}
}
Future<String> _asyncDidCatchException(Function fn) async {
var caught = true;
try {
await fn();
} on Exception catch (_) {
caught = false;
}
if (caught == true) {
return _passed;
} else {
return _noCatch;
}
}
const _typoMessage = 'Test failed! Check for typos in your return value';
const _passed = 'PASSED';
const _noCatch = 'NO_CATCH';
const _halfSecond = Duration(milliseconds: 500);
String _failOnce() {
if (_logoutSucceeds) {
return 'Success!';
} else {
_logoutSucceeds = true;
throw Exception('Logout failed');
}
}
bool _logoutSucceeds = false;
解答
String addHello(String user) => 'Hello $user';
Future<String> greetUser() async {
final username = await fetchUsername();
return addHello(username);
}
Future<String> sayGoodbye() async {
try {
final result = await logoutUser();
return '$result Thanks, see you next time';
} catch (e) {
return 'Failed to logout user: $e';
}
}
哪些 lints 適用於 Future?
#為了捕捉在使用 async 和 Future 時出現的常見錯誤,請啟用以下 lints
下一步?
#恭喜,您已完成本教學課程!如果您想瞭解更多資訊,以下是一些後續建議
- 試用 DartPad。
- 嘗試另一個教學課程。
- 瞭解更多關於 Dart 中的 Future 和非同步程式碼的資訊
- Stream 教學課程:學習如何使用一系列非同步事件。
- Dart 中的並行:瞭解並學習如何在 Dart 中實作並行。
- 非同步支援:深入瞭解 Dart 的語言和函式庫對非同步程式碼的支援。
- 來自 Google 的 Dart 影片:觀看一個或多個關於非同步程式碼的影片。
- 取得 Dart SDK!
除非另有說明,否則本網站上的文件反映 Dart 3.7.1。頁面最後更新於 2025-02-12。 檢視原始碼 或 回報問題。