Dart API 學(xué)習(xí)記錄(一) http://www.itdecent.cn/p/388d986c0f48
Dart API 學(xué)習(xí)記錄(二)
dart:async庫
dart:async庫是Dart 語言支持異步編程的基礎(chǔ)。
庫中提供了Future 類和Stream 類。這兩個類是理解Dart 語言異步機(jī)制的基礎(chǔ)。
在使用前async 庫的功能前需要在代碼中添加如下語句:
import 'dart:async'
Future 類
Future 對象返回值表明當(dāng)前時刻的計算結(jié)果可能還不可用。
Future 實例會在計算結(jié)束后再返回結(jié)果,而不是現(xiàn)在立即返回。
Future 通常用來操作冗長的計算,比如大規(guī)模的I/O 操作或與用戶進(jìn)行交互。
下面的例子將返回一個Future:
import 'dart:io';
void bindServer(){
HttpServer.bind('127.0.0.1', 4444)
.then((server) => print('${server.isBroadcast}'))
.catchError(print);
}
void main(){
bindServer();
}
在例子中Future.then注冊了一個回調(diào)函數(shù),當(dāng)bind() 方法執(zhí)行完成后開始運行?;卣{(diào)函數(shù)的功能是: 當(dāng)bind()執(zhí)行成功后會返回一個HttpServer 的對象,在回調(diào)函數(shù)中會返回該HttpServer的一個熟悉值。
Stream 類
一個Stream 對象提供了一個異步數(shù)據(jù)的隊列。在這個隊列中可能包含事件(鼠標(biāo)點擊),大數(shù)據(jù)塊.
利用Stream 方法來讀取文件的例子:
import 'dart:async';
import 'dart:io';
import 'dart:convert';
void readFile(f){
Stream<List<int>> stream = new File(f).openRead();
stream.transform(UTF8.decoder).listen(print);
}
void main(){
String filename = "/opt/program/Dart/dart-code/test_data/temp.py";
readFile(filename);
Asynchronous Programming: Futures
以下的內(nèi)容來源于下列地址:
https://www.dartlang.org/tutorials/language/futures
要點總結(jié)
- Dart 語言是單線程的。
- 同步的代碼可能導(dǎo)致代碼阻塞。
- 可以使用Future 方法來執(zhí)行異步操作。
- 在異步方法里使用
await()來暫停代碼執(zhí)行,直到Future 執(zhí)行完成。 - 也可使用Future 的
then()方法來實現(xiàn)上面的目的。 - 在異步方法里使用
try...catch...來獲取異常。 - 也可是使用Future 的
catchError()方法來實現(xiàn)上面的目的。 - 可以就多個異步的方法鏈接起來順序執(zhí)行。
簡介
可能導(dǎo)致代碼阻塞的例子:
void printDailyNewsDigest(){
// Synchronous code
String news = gatherNewsReports(); // Can take a while.
print(news);
}
String gatherNewsReports(){
return "<gathered news goes here>";
}
String printWinningLotteryNumbers(){
return "Winning lotto numbers: [23, 63, 87, 26, 2]";
}
String printWeatherForecast(){
return "Tomorrow's forecast: 70F, sunny";
}
String printBaseballScore(){
return "Baseball score: Red Sox 10, Yankees 0";
}
void main(){
printDailyNewsDigest();
printWinningLotteryNumbers();
printWeatherForecast();
printBaseballScore();
}
對應(yīng)輸出為:
<gathered news goes here>
Winning lotto numbers: [23, 63, 87, 26, 2]
Tomorrow's forecast: 70F, sunny
Baseball score: Red Sox 10, Yankees 0
使用說明
Future 代表著在將來會獲取到一個值。
當(dāng)Future 被調(diào)用時,會發(fā)生:
- 將需要完成的工作進(jìn)行隊列化并返回一個Future 的對象。
- 當(dāng)待執(zhí)行的工作完成并獲取到最后的值后,F(xiàn)uture 對象以這個值結(jié)束。
可以通過下面的方式獲取Future 的返回值。
- 使用async 和 await
- 使用Future 提供的API
使用async 和 await實現(xiàn)異步通信
async 和 await 是Dart 語言中異步編程支持的關(guān)鍵字。使用這兩個關(guān)鍵字可以在不使用Future API 的情況下實現(xiàn)異步編程。
使用async 和 await實現(xiàn)異步通信的例子:
import 'dart:html';
import 'dart:async';
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');
}
// Imagine that this function is more complex and slow. :)
Future gatherNewsReports_async() async {
String path = 'https://www.dartlang.org/f/dailyNewsDigest.txt';
return (await HttpRequest.getString(path));
}
Future printDailyNewsDigest_async() async {
String news = await gatherNewsReports_async();
print(news);
}
void asynchronousCode(){
printDailyNewsDigest_async();
printWinningLotteryNumbers();
printWeatherForecast();
printBaseballScore();
}
main() {
asynchronousCode();
}
上面的例子對應(yīng)的輸出結(jié)果:
Winning lotto numbers: [23, 63, 87, 26, 2]
Tomorrow's forecast: 70F, sunny.
Baseball score: Red Sox 10, Yankees 0
<gathered news goes here>
上面代碼的執(zhí)行流程圖:

- 程序開始執(zhí)行。
- 主函數(shù)調(diào)用
asynchronousCode()函數(shù)。 -
asynchronousCode()函數(shù)調(diào)用printDailyNewsDigest_async()函數(shù),然后函數(shù)立即返回一個Future 對象。 -
asynchronousCode()函數(shù)其它中接下來的代碼依次開始執(zhí)行。以此執(zhí)行printWinningLotteryNumbers(),printWeatherForecast(),printBaseballScore()函數(shù),并返回對應(yīng)的結(jié)果。 -
printDailyNewsDigest_async()函數(shù)體開始執(zhí)行。 - 當(dāng)執(zhí)行到await 語句時:程序暫停,等待
gatherNewsReports_async()函數(shù)執(zhí)行結(jié)果。 - 一旦
gatherNewsReports_async()函數(shù)的Future 執(zhí)行完成,printDailyNewsDigest_async()函數(shù)將繼續(xù)執(zhí)行。 - 當(dāng)
printDailyNewsDigest_async()函數(shù)執(zhí)行完成后,程序結(jié)束。
處理error
如果Future 的函數(shù)執(zhí)行中發(fā)生了error,怎Future 的返回值中就會包含error 信息。
可以通過try-catch 語句來處理這些異常。
處理異常的例子:
import 'dart:html';
import 'dart:async';
Future printDailyNewsDigest() async {
try {
String news = await gatherNewsReports();
print(news);
} catch (e) {
// ... handle error ...
}
}
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');
}
// Imagine that this function is more complex and slow. :)
Future gatherNewsReports() async {
String path =
'https://www.dartlang.org/f/dailyNewsDigest.txt';
String content = await HttpRequest.getString(path);
return content;
}
順序執(zhí)行
可以使用多個await 語句來確認(rèn)上一條一句結(jié)束了才執(zhí)行下面的語句。
例子如下:
// Sequential processing using async and await.
main() async {
await expensiveA();
await expensiveB();
doSomethingWith(await expensiveC());
}
Future API 實現(xiàn)異步通信
await / async 在Dart 1.9版本里面才添加進(jìn)來。
可以通過`then() 方法來注冊一個回調(diào)函數(shù)。這個回調(diào)函數(shù)將在Future 完成后開始執(zhí)行。
處理error
在Future API 中提供了catchError()方法來處理Future 執(zhí)行中的異常。
調(diào)用多個Future 類型的返回值
使用多個then() 方法來鏈接多個函數(shù)。
expensiveA().then((aValue) => expensiveB())
.then((bValue) => expensiveC())
.then((cValue) => doSomethingWith(cValue));
上面的代碼等待expensiveA 執(zhí)行完成后開始執(zhí)行expensiveB, 在expensiveB doSomethingWith。
調(diào)用Future.wait() 方法來鏈接多個函數(shù)。
// Parallel processing using the Future API
Future.wait([expensiveA(), expensiveB(), expensiveC()])
.then((List responses) => chooseBestResponse(responses))
.catchError((e) => handleError(e));
上面的代碼等待expensiveA(), expensiveB(), expensiveC() 都執(zhí)行完成后才開始執(zhí)行then() 中的例子。
Asynchronous Programming: Streams
以下的內(nèi)容來源于下列地址:
https://www.dartlang.org/tutorials/language/streams
要點
- Stream 提供數(shù)據(jù)的異步隊列
- 數(shù)據(jù)隊列里面包含用戶交互數(shù)據(jù),或從文件中讀取的數(shù)據(jù)。
- 可以使用
await for或listen()來處理stream 對象的數(shù)據(jù)。 - Stream API 提供錯誤處理。
- 包含兩種類型的Stream:單個訂閱,廣播Stream
接受Stream 事件
雖然Stream 對象的創(chuàng)建有很多種方法,但是使用方法卻是相同的。使用方法: 使用 await for語句去操作一個Stream 對象的事件的迭代對象,就像for 循環(huán)操作迭代對象一樣。
await for語句只能操作使用async關(guān)鍵字標(biāo)記的函數(shù)。
例子如下:
import 'dart:async';
Future<int> sumStream(Stream<int> stream) async {
var sum = 0;
await for (var value in stream) {
sum += value;
}
return sum;
}
Stream<int> countStream(int to) async* {
for (int i = 1; i <= to; i++) {
yield i;
}
}
main() async {
var stream = countStream(10);
var sum = await sumStream(stream);
print(sum); // 55
}
錯誤事件
當(dāng)沒有收到新的事件時,Stream 對象會結(jié)束運行。
當(dāng)接受到新事件的時候,系統(tǒng)胡通知Stream 收到了新的事件。
當(dāng)讀取到的Stream 事件在await for 循環(huán)執(zhí)行時,當(dāng)Stream 結(jié)束后,await for 循環(huán)也就結(jié)束了。
當(dāng)Stream 對象運行過程中有錯誤發(fā)生,大部分的Stream 將停止。同時Stream 對象將至少發(fā)送一條錯誤信息。
可以使用try-catch 語句間await for 語句包括起來,用這種方法來處理error 信息。
處理Stream 錯誤的例子:
import 'dart:async';
Future<int> sumStream(Stream<int> stream) async {
var sum = 0;
try {
await for (var value in stream) {
sum += value;
}
} catch (error) {
return -1;
}
return sum;
}
Stream<int> countStream(int to) async* {
for (int i = 1; i <= to; i++) {
if (i == 4) {
throw "Whoops!"; // Intentional error
} else {
yield i;
}
}
}
main() async {
var stream = countStream(10);
var sum = await sumStream(stream);
print(sum); // -1
}
Stream 對象處理
可以很方便的通過Stream 的await for 循環(huán)來依次處理相應(yīng)的事件。
下面的例子,可以方便找到 數(shù)值 > 0的數(shù)值的下標(biāo)位置。
import 'dart:async';
Future<int> lastPositive(Stream<int> stream) async {
var lastValue = null;
await for (var value in stream) {
if (value < 0) continue;
lastValue = value;
}
return lastValue;
}
main() async {
var data = [1, -2, 3, -4, 5, -6, 7, -8, 9, -10];
var stream = new Stream.fromIterable(data);
var lastPos = await lastPositive(stream);
print(lastPos); // 9
}
Stream 兩種類型
單個訂閱類型 (Single subscription streams)
大部分的Stream 實例會包含整個事件隊列的一部分。每一個事件必須在正確的位置,并且不允許有任何數(shù)據(jù)遺失。這種類型類似于閱讀文件或接受網(wǎng)頁服務(wù)。
這是類似于TCP socket,不允許有數(shù)據(jù)遺失。
這種類型的Stream,只能被監(jiān)聽一次。
假如再次監(jiān)聽,將會導(dǎo)致之前的事件隊列丟失,并導(dǎo)致Stream 不再接受數(shù)據(jù)。
廣播類型(Broadcast streams)
這種類型的Stream 可以只一次處理一個事件。
可以對這種類型實施多次監(jiān)聽。
Stream 的處理方法
完整方法列表:
| 返回值 | 函數(shù) |
|---|---|
Future<T\> |
get first |
Future<T> |
get last |
Future<T> |
get single |
Future<int> |
get length |
Future<bool> |
get isEmpty |
Future<T> |
firstWhere(bool test(T event), {T orElse()}) |
Future<T> |
lastWhere(bool test(T event), {T orElse()}) |
Future<T> |
singleWhere(bool test(T event), {T orElse()}) |
Future<T> |
reduce(T combine(T previous, T element)) |
Future |
fold(initialValue, combine(previous, T element)) |
Future<String> |
join([String separator = ""]) |
Future<bool> |
contains(Object needle) |
Future |
forEach(void action(T element)) |
Future<bool> |
every(bool test(T element)) |
Future<bool> |
any(bool test(T element)) |
Future<List<T>> |
toList() |
Future<Set<T>> |
toSet() |
Future<T> |
elementAt(int index) |
Future |
pipe(StreamConsumer<T> consumer) |
Future |
drain([var futureValue]) |
簡單的實例:
Future<bool> contains(Object element) async {
await for (var event in this) {
if (event == element) return true;
}
return false;
}
Future forEach(void action(T element)) async {
await for (var event in this) {
action(event);
}
}
Future<List<T>> toList() async {
var result = <T>[];
await this.forEach(result.add);
return result;
}
Future<String> join([String separator = ""]) async {
return (await this.toList()).join(separator);
}
Stream 對象的修改方法
方法列表:
| 返回值 | 函數(shù) |
|---|---|
Stream<T> |
where(bool test(T event)) |
Stream |
map(convert(T event)) |
Stream |
expand(Iterable expand(T element); |
Stream<T> |
take(int count) |
Stream<T> |
skip(int count) |
Stream<T> |
takeWhile(bool test(T element)) |
Stream<T> |
skipWhile(bool test(T element)) |
Stream<T> |
distinct([bool equals(T previous, T next)]) |
Stream |
asyncExpand(Stream expand(T element)) |
Stream |
asyncMap(Future convert(T event)) |
Stream |
timeout(Duration timeLimit, {void onTimeout(EventSink sink)}) |
Stream<T> |
handleError(Function onError, {bool test(error)}) |
Stream |
transform(StreamTransformer<T, dynamic> streamTransformer) |
使用這些函數(shù),閱讀文件的例子:
import 'dart:io';
import 'dart:async';
import 'dart:convert';
main(args) async {
var file = new File(args[0]);
var lines = file
.openRead()
.transform(UTF8.decoder)
.transform(const LineSplitter());
await for (var line in lines) {
if (!line.startsWith('#')) {
print(line);
}
}
}
The listen() method
Stream 的listen() 方法是更底層的方法,其它的Stream的方法都是在listen 之上定義的。
創(chuàng)建一個Stream 類型時,可以只繼承Stream 類,并實現(xiàn)listen 方法。