Dart API 学习记录(二)

Dart API 学习记录(一) http://www.jianshu.com/p/388d986c0f48

Dart API 学习记录(二)

dart:async

dart:async库是Dart 语言支持异步编程的基础。
库中提供了Future 类和Stream 类。这两个类是理解Dart 语言异步机制的基础。

在使用前async 库的功能前需要在代码中添加如下语句:

import 'dart:async'

Future 类

Future 对象返回值表明当前时刻的计算结果可能还不可用。
Future 实例会在计算结束后再返回结果,而不是现在立即返回。

Future 通常用来操作冗长的计算,比如大规模的I/O 操作或与用户进行交互。

下面的例子将返回一个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注册了一个回调函数,当bind() 方法执行完成后开始运行。回调函数的功能是: 当bind()执行成功后会返回一个HttpServer 的对象,在回调函数中会返回该HttpServer的一个熟悉值。

Stream 类

一个Stream 对象提供了一个异步数据的队列。在这个队列中可能包含事件(鼠标点击),大数据块.

利用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

以下的内容来源于下列地址:
https://www.dartlang.org/tutorials/language/futures

要点总结

  • Dart 语言是单线程的。
  • 同步的代码可能导致代码阻塞。
  • 可以使用Future 方法来执行异步操作。
  • 在异步方法里使用await()来暂停代码执行,直到Future 执行完成。
  • 也可使用Future 的then()方法来实现上面的目的。
  • 在异步方法里使用try...catch...来获取异常。
  • 也可是使用Future 的catchError()方法来实现上面的目的。
  • 可以就多个异步的方法链接起来顺序执行。

简介

可能导致代码阻塞的例子:

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();
}

对应输出为:

<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 代表着在将来会获取到一个值。
当Future 被调用时,会发生:

  1. 将需要完成的工作进行队列化并返回一个Future 的对象。
  2. 当待执行的工作完成并获取到最后的值后,Future 对象以这个值结束。

可以通过下面的方式获取Future 的返回值。

  1. 使用async 和 await
  2. 使用Future 提供的API

使用async 和 await实现异步通信

async 和 await 是Dart 语言中异步编程支持的关键字。使用这两个关键字可以在不使用Future API 的情况下实现异步编程。

使用async 和 await实现异步通信的例子:

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();
}

上面的例子对应的输出结果:

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

上面代码的执行流程图:


async-await
  1. 程序开始执行。
  2. 主函数调用asynchronousCode()函数。
  3. asynchronousCode()函数调用printDailyNewsDigest_async()函数,然后函数立即返回一个Future 对象。
  4. asynchronousCode()函数其它中接下来的代码依次开始执行。以此执行printWinningLotteryNumbers()printWeatherForecast()printBaseballScore()函数,并返回对应的结果。
  5. printDailyNewsDigest_async()函数体开始执行。
  6. 当执行到await 语句时:程序暂停,等待gatherNewsReports_async()函数执行结果。
  7. 一旦gatherNewsReports_async()函数的Future 执行完成,printDailyNewsDigest_async()函数将继续执行。
  8. printDailyNewsDigest_async()函数执行完成后,程序结束。

处理error

如果Future 的函数执行中发生了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;
}

顺序执行

可以使用多个await 语句来确认上一条一句结束了才执行下面的语句。

例子如下:

// Sequential processing using async and await.
main() async {
  await expensiveA();
  await expensiveB();
  doSomethingWith(await expensiveC());
}

Future API 实现异步通信

await / async 在Dart 1.9版本里面才添加进来。

可以通过`then() 方法来注册一个回调函数。这个回调函数将在Future 完成后开始执行。

处理error

在Future API 中提供了catchError()方法来处理Future 执行中的异常。

调用多个Future 类型的返回值

使用多个then() 方法来链接多个函数。
expensiveA().then((aValue) => expensiveB())
            .then((bValue) => expensiveC())
            .then((cValue) => doSomethingWith(cValue));

上面的代码等待expensiveA 执行完成后开始执行expensiveB, 在expensiveB doSomethingWith。

调用Future.wait() 方法来链接多个函数。
// Parallel processing using the Future API
Future.wait([expensiveA(), expensiveB(), expensiveC()])
      .then((List responses) => chooseBestResponse(responses))
      .catchError((e) => handleError(e));

上面的代码等待expensiveA(), expensiveB(), expensiveC() 都执行完成后才开始执行then() 中的例子。

Asynchronous Programming: Streams

以下的内容来源于下列地址:
https://www.dartlang.org/tutorials/language/streams

要点

  1. Stream 提供数据的异步队列
  2. 数据队列里面包含用户交互数据,或从文件中读取的数据。
  3. 可以使用await forlisten()来处理stream 对象的数据。
  4. Stream API 提供错误处理。
  5. 包含两种类型的Stream:单个订阅,广播Stream

接受Stream 事件

虽然Stream 对象的创建有很多种方法,但是使用方法却是相同的。使用方法: 使用 await for语句去操作一个Stream 对象的事件的迭代对象,就像for 循环操作迭代对象一样。

await for语句只能操作使用async关键字标记的函数。
例子如下:

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
}

错误事件

当没有收到新的事件时,Stream 对象会结束运行。
当接受到新事件的时候,系统胡通知Stream 收到了新的事件。
当读取到的Stream 事件在await for 循环执行时,当Stream 结束后,await for 循环也就结束了。

当Stream 对象运行过程中有错误发生,大部分的Stream 将停止。同时Stream 对象将至少发送一条错误信息。
可以使用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 循环来依次处理相应的事件。

下面的例子,可以方便找到 数值 > 0的数值的下标位置。

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 实例会包含整个事件队列的一部分。每一个事件必须在正确的位置,并且不允许有任何数据遗失。这种类型类似于阅读文件或接受网页服务。
这是类似于TCP socket,不允许有数据遗失。

这种类型的Stream,只能被监听一次。
假如再次监听,将会导致之前的事件队列丢失,并导致Stream 不再接受数据。

广播类型(Broadcast streams)

这种类型的Stream 可以只一次处理一个事件。
可以对这种类型实施多次监听。

Stream 的处理方法

完整方法列表:

返回值 函数
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 对象的修改方法

方法列表:

返回值 函数
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)

使用这些函数,阅读文件的例子:

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 之上定义的。

创建一个Stream 类型时,可以只继承Stream 类,并实现listen 方法。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,099评论 18 139
  • 异步编程对JavaScript语言太重要。Javascript语言的执行环境是“单线程”的,如果没有异步编程,根本...
    呼呼哥阅读 7,253评论 5 22
  • 本文是对 Dart 语言的官方文档做了简单的翻译和总结,有不当之处敬请指正。如果有时间和精力建议通读官方文档 he...
    小小小超子阅读 10,358评论 8 22
  • 此文章是v1.0+时编写,年代久远,小心有毒,谨慎食用!!! 一些重要概念 所有的东西都是对象,所有的对象都是类的...
    soojade阅读 10,001评论 2 27
  • 和Lydia去看了期待已久的乌托邦
    我说你瞅啥呀阅读 101评论 0 0