【譯】異步編程:Futures

關鍵點

  • dart是單線程語言
  • 同步代碼會阻塞你的程序
  • 使用Future對象來執行異步操做
  • 在async函數裏使用await關鍵字來掛起執行知道一個Future操做完成
  • 或者使用then()方法
  • 在async函數裏使用try-catch表達式捕獲錯誤
  • 或者使用catchError()方法
  • 能夠使用鏈式操做future對象來按順序執行異步函數

dart是一個單線程的編程語言,若是編寫了任何阻塞執行線程的代碼(例如耗時計算或者I/O),程序就會被阻塞。異步操做可讓你在等待一個操做完成的同時完成其餘工做。Dart使用Future對象來進行異步操做。html

介紹

先來看一個會致使阻塞的程序代碼:web

// Synchronous code
void printDailyNewsDigest() {
  var newsDigest = gatherNewsReports(); // Can take a while.
  print(newsDigest);
}

main() {
  printDailyNewsDigest();
  printWinningLotteryNumbers();
  printWeatherForecast();
  printBaseballScore();
}
複製代碼

程序會收集當天的新聞而且打印出來(耗時操做),而後打印一些用戶感興趣的其餘信息。編程

<gathered news goes here>
Winning lotto numbers: [23, 63, 87, 26, 2]
Tomorrow's forecast: 70F, sunny. Baseball score: Red Sox 10, Yankees 0 複製代碼

這段代碼是有問題的,當printDailyNewsDigest()方法作耗時操做阻塞,剩下的其餘代碼不管多長時間都只有等到printDailyNewsDigest()返回結果以後才能繼續執行。api

爲了幫助保持應用程序的響應,Dart庫的做者在定義可能作耗時工做的函數時用了異步模型。這些函數的返回值是一個future。bash

future是什麼

一個future是一個Future的泛型Future<T>對象,表明了一個異步操做產生的T類型的結果。若是結果的值不可用,future的類型會是Future<void>,當返回一個future的函數被調用了,將會發生以下兩件事: 1.這個函數加入待完成的隊列而且返回一個未完成的Future對象。 2.接着,當這個操做結束了,Future對象返回一個值或者錯誤。異步

編寫返回一個future的代碼時,有兩面兩個可選方法:async

  • 使用async和await關鍵字
  • 使用FutureAPI

async 和 await

asyncawait 關鍵字是Dart語言異步支持的一部分。容許你不使用Future api像編寫同步代碼同樣編寫異步代碼。一個異步函數的函數體前面要生命async關鍵字,await關鍵字僅在異步函數裏生效。編程語言

下面的代碼就是一個使用了Future api的例子。ide

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);
複製代碼

main()方法裏面printDailyNewsDigest()是一個被調用的,可是由於等待了一秒延遲執行,因此在最後才被打印出來。這個程序的執行順序以下:函數

1.程序開始執行

2.main方法調用printDailyNewsDigest(),可是不會當即返回,會調用gatherNewsReports()

3.gatherNewsReports()開始收集新聞同時返回一個Future

4.printDailyNewsDigest()使用then()指定一個Future的響應結果,調用then()返回一個新完成的Future結果做爲他的回調。

5.剩下的print方法是同步的會依次執行。

6.當全部的新聞都被收集到了,帶有新聞信息的Future被gatherNewsReports()返回。

7.then()方法執行,將future返回的String做爲參數傳遞給print打印。

future.then(print)等同於future.then((newsDigest) => print(newsDigest))

因此printDailyNewsDigest()還能夠寫成:

Future<void> printDailyNewsDigest() {
  final future = gatherNewsReports();
  return future.then((newsDigest) {
    print(newsDigest);
    // Do something else...
  });
}
複製代碼

newsDigest是gatherNewsReports()返回的結果。

即時這個Future的類型是Future<void>,也須要傳遞一個參數給then()的回調,直接用下劃線_表示。

Future<void> printDailyNewsDigest() {
  final future = gatherNewsReports();
  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);
複製代碼

若是請求不到新聞而且失敗了,上面的代碼將會執行:

1.gatherNewsReports()完成返回的future攜帶了error

2.then()方法中的print不會被執行

3.catchError()捕獲處處理錯誤,future被catchError()正常完成並返回,錯誤不會繼續傳遞下去。

更多細節閱讀 Futures and Error Handling

調用多個函數返回futures

有三個函數,expensiveA()expensiveB()expensiveC(), 它們都返回Future對象。你能夠順序調用它們(one by one),或者也能夠同時調用三個函數而且當全部結果都返回時再作處理。Future api支持以上的操做。

用then()進行鏈式調用

按順序使用then()調用每一個方法

expensiveA()
    .then((aValue) => expensiveB())
    .then((bValue) => expensiveC())
    .then((cValue) => doSomethingWith(cValue));
複製代碼

這是個嵌套調用,上一個函數的返回結果做爲下一個函數的參數。

使用Future.wait()等待多個方法一塊兒完成

若是好幾個函數的執行順序可有可無,能夠使用Future.wait()

Future.wait([expensiveA(), expensiveB(), expensiveC()])
    .then((List responses) => chooseBestResponse(responses, moreInfo))
    .catchError(handleError);
複製代碼

當傳遞一個future列表給Future.wait()時,它會當即返回一個Future,可是直到列表裏的future都完成的時候,這個Future纔會完成,他會返回一個列表裏面是每個future產生的結果數據。

若是任何一個調用的函數產生了錯誤,都會被catchError()捕獲到去處理錯誤。

相關資料

相關文章
相關標籤/搜索