Dart代碼運行在一個已執行的線程內。若是Dart代碼阻塞,例如,運行一個長時間運算或者等待I/O操做,程序將要凍結。 異步操做可讓你在等待一個操做完成時,進行其餘工做。Dart使用Future對象(Futures)表示異步執行結果。 你可使用async和await也可使用Future API,進行futures開發。html
Node: 全部Dart代碼運行在isolate上下文中,這個isolate擁有Dart代碼全部內存。當Dart代碼執行時,isolate內不能運行其餘代碼。 若是你想多部分Dart代碼同時運行,你能運行他們在其餘的isolate(Web 用workers代替 isolate)。多個isolate同時運行,一般運行在各自的CPU內核上。isolate不共享內存,他們之間的惟一溝通方式是發送消息。關於isolate更多內容,請查看文檔isolates或者web workersweb
讓咱們看一些可能致使程序凍結的代碼:編程
// Synchronous code
void printDailyNewsDigest() {
var newsDigest = gatherNewsReports(); // Can take a while.
print(newsDigest);
}
main() {
printDailyNewsDigest();
printWinningLotteryNumbers();
printWeatherForecast();
printBaseballScore();
}
複製代碼
咱們的程序收集當天的新聞,並打印它,而後打印一些用戶感興趣的其餘項目:api
<gathered news goes here>
Winning lotto numbers: [23, 63, 87, 26, 2]
Tomorrow's forecast: 70F, sunny. Baseball score: Red Sox 10, Yankees 0 複製代碼
咱們的代碼是有潛在問題的:因爲gatherNewsReports()阻塞,餘下的代碼只能一直等待gatherNewsReports()從文件讀取返回值以後執行。若是讀取文件花費很長時間,用戶必須等,儘管用戶可能更想知道他們是否贏了彩票,明天的天氣是什麼,誰贏了今天的比賽。bash
爲了保證程序響應,Dart庫做者處理耗時操做時使用異步模式,這些函數使用future做爲返回值。異步
#Future 是什麼? future是一個Future對象,它表示一個異步操做產生一個T類型的結果。若是結果爲不可用的值,返回類型爲Future。當一個返回futrue的函數被調用時,有兩件事發生:async
使用future,有兩種方式:ide
async和await是Dart支持異步編程的一部分。他們容許你寫異步代碼,看起來像同步代碼而且不須要使用Future API。 異步函數便是將async關鍵字放在函數體前便可。await關鍵字只用在async函數。 下面程序使用async和await模擬從 www.dartlang.org. 讀取內容:異步編程
// 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<void> 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 = '<gathered news goes here>';
const oneSecond = Duration(seconds: 1);
// Imagine that this function is more complex and slow. :)
Future<String> 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<String> 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 <gathered news goes here> 複製代碼
printDailyNewsDigest()是第一個被調用的,雖然只是輸出一行,可是新聞也是最後被打印的。這是由於運行和打印代碼是異步運行的。
在這個例子中,printDailyNewsDigest()調用非阻塞gatherNewsReports(),調用gatherNewsReports()會將執行任務加入隊列,但不會阻止剩下代碼執行。程序打印彩票號碼,預測棒球比賽分數。當gatherNewsReports() 完成得到新聞後,打印它。若是gatherNewsReports()花費一些時間完成,因爲異步執行也不會給用戶帶來很大的影響。在打印每日新聞以前用戶能夠閱讀其餘消息。
請留意返回類型, gatherNewsReports()的返回類型是Future,這意味這個返回值是一個以字符future。printDailyNewsDigest() 無返回值,它的返回類型爲Future。
下圖展現調用流程,數字和步驟相互對應:
注意,異步函數當即(同步地)開始執行。當第一次出現一下狀況時,函數暫停執行,並返回一個未完成的future:
異步函數使用try-cache進行錯誤處理。
Future<void> printDailyNewsDigest() async {
try {
var newsDigest = await gatherNewsReports();
print(newsDigest);
} catch (e) {
// Handle error...
}
}
複製代碼
try-cache的行爲在異步代碼和同步代碼中是相同的,若是try塊中的代碼拋出異常,則catch子句中的代碼將執行。
您可使用多個await表達式來確保順序執行,即每一個語句在執行下一個語句以前完成:
// Sequential processing using async and await.
main() async {
await expensiveA();
await expensiveB();
doSomethingWith(await expensiveC());
}
複製代碼
expensiveA()執行完成後,纔會執行expensiveB().
#Future API 在Dart 1.9中添加async和await以前,您必須使用Future API。目前仍然可能在老的Dart代碼中以及須要比asyn-await提供更豐富功能的代碼中,看到Future API。
使用Future API寫異步代碼,用then()註冊回調。當Future完成後,回調被執行。
下面程序使用Future API模擬從 www.dartlang.org. 讀取內容:
// 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<void> 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 = '<gathered news goes here>'; const oneSecond = Duration(seconds: 1); // Imagine that this function is more complex and slow. :) Future<String> 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<String> 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 <gathered news goes here> 複製代碼
printDailyNewsDigest()是第一個被調用的,雖然只是輸出一行,可是新聞也是最後被打印的。這是由於運行和打印代碼是異步運行的。
程序執行步驟:
Node : 在printDailyNewsDigest()函數,future.then(print) 等價於: future.then((newsDigest) => print(newsDigest))
另外,then()內部代碼可使用{}:
Future<void> printDailyNewsDigest() {
final future = gatherNewsReports();
return future.then((newsDigest) {
print(newsDigest);
// Do something else...
});
}
複製代碼
你須要提供一個參數給then()的回調,即便Future是Future類型。按照慣例,一個無用參數使用下劃線表示。
final future = printDailyNewsDigest();
return future.then((_) {
// Code that doesn't use the `_` parameter... print('All reports printed.'); }); 複製代碼
##錯誤處理 使用Future API,你能用catchError()捕獲錯誤。
Future<void> printDailyNewsDigest() =>
gatherNewsReports().then(print).catchError(handleError);
複製代碼
若是新聞數據讀取無效,代碼的執行流程以下:
鏈式模式是Future API的常見模式。能夠將Future API 等同於try-catch模塊。
與then()相似,catchError()返回一個新的Future,它是回調的返回值。 更多詳細信息和例子,請參考Futures and Error Handling.
考慮三個函數,expensiveA(), expensiveB(), 和expensiveC(),這三個函數都返回Future對象。你能順序調用他們,或者你能同時開始他們,並在全部函數執行完成後作一些事情。Future接口能夠輕鬆的處理這兩種用例。
當函數須要按順序執行時,使用鏈式then():
expensiveA()
.then((aValue) => expensiveB())
.then((bValue) => expensiveC())
.then((cValue) => doSomethingWith(cValue));
複製代碼
嵌套回調雖然能夠工做,可是難於閱讀。
若是函數的執行順序不重要,你能夠用Future.wait()。
當你傳遞一個future列表給Future.wait(),它馬上返回一個未完成的future。直到給定的future列表所有執行完這個future才完成。future的返回值,由列表中的每一個future的返回值組成。
Future.wait([expensiveA(), expensiveB(), expensiveC()])
.then((List responses) => chooseBestResponse(responses, moreInfo))
.catchError(handleError);
複製代碼
若是任何一個函數返回錯誤,Future.wait()的futrue都以錯誤結束。使用catchError()處理錯誤。
#其餘資源 閱讀如下文檔,瞭解有關在Dart中使用future和異步編程的更多詳細信息: