本教程展示了如何使用下列库的主要功能,这些库包含在所有Dart平台中:
[TOC]
[dart:core](#)
内置类型、集合和其他核心功能。这个库被自动导入到每个Dart程序中。
[dart:async](#)
支持异步编程,使用诸如Future和Stream之类的类。
[dart:math](#)
数学常数和函数,和随机数发生器。
[dart:convert](#)
用于在不同数据表示之间转换的编码器和解码器,包括JSON和UTF-8。
这个页面只是一个概述;它只覆盖了几个dart:*库,没有第三方库。特定于平台的dart:io和dart:html库都包含在dart:io tour和dart:html tour中。
其他可以找到库信息的地方是[pub.darlang.org](https://pub.dartlang.org/)和[Dart web developer library guide](https://webdev.dartlang.org/guides/web-programming)。您可以在dart API引用中找到所有dart:* [库的API文档](https://api.dartlang.org/stable),如果您正在使用Flutter,则可以找到[Flutter API文档](https://docs.flutter.io/)。
>DartPad提示:你可以把本页的代码复制到[DartPad](https://dartpad.dartlang.org/)上运行。
>
# dart:core - numbers, collections, strings, 等
dart:core 库 ([API文档](https://api.dartlang.org/stable/dart-core/dart-core-library.html))提供了一组小型但关键的内置功能。这个库被自动导入到每个Dart程序中。
## 向控制台打印
顶级print()方法接受单个参数(任何对象),并在控制台中显示该对象的字符串值(由toString()返回)。
~~~
print(anObject);
print('I drink $tea.');
~~~
有关基本字符串和toString()的更多信息,请参阅语言教程中的[Strings](https://www.dartlang.org/guides/language/language-tour#strings)。
## Numbers
dart:core库定义了num、int和double类,它们有一些处理数字的基本工具。
您可以使用int和double的parse()方法将字符串转换为整数或双精度浮点数:
~~~
assert(int.parse('42') == 42);
assert(int.parse('0x42') == 66);
assert(double.parse('0.50') == 0.5);
~~~
或者使用num的parse()方法,它在可能的情况下创建一个整数,否则创建一个double:
~~~
assert(num.parse('42') is int);
assert(num.parse('0x42') is int);
assert(num.parse('0.50') is double);
~~~
要指定整数的基数,添加一个基数参数:
~~~
assert(int.parse('42', radix: 16) == 66);
~~~
使用toString()方法将int或double转换为字符串。要指定小数点右边的位数,使用[toStringAsFixed()](https://api.dartlang.org/stable/dart-core/num/toStringAsFixed.html)。要指定字符串中的有效位数,使用[toStringAsPrecision()](https://api.dartlang.org/stable/dart-core/num/toStringAsPrecision.html):
~~~
// Convert an int to a string.
assert(42.toString() == '42');
// Convert a double to a string.
assert(123.456.toString() == '123.456');
// Specify the number of digits after the decimal.
assert(123.456.toStringAsFixed(2) == '123.46');
// Specify the number of significant figures.
assert(123.456.toStringAsPrecision(2) == '1.2e+2');
assert(double.parse('1.2e+2') == 120.0);
~~~
有关更多信息,请参阅[int](https://api.dartlang.org/stable/dart-core/int-class.html)、[double](https://api.dartlang.org/stable/dart-core/double-class.html)和[num](https://api.dartlang.org/stable/dart-core/num-class.html)的API文档。也可以看[dart:math](https://www.dartlang.org/guides/libraries/library-tour#dartmath---math-and-random)部分
## 字符串和正则表达式
Dart中的字符串是UTF-16代码单元的不可变序列。 本文档的其他章节有关于字符串的更多信息。 您可以使用正则表达式(RegExp对象)在字符串中搜索并替换部分字符串。
String类定义了诸如split()、contains()、startsWith()、endsWith()等方法。
### 在字符串中搜索
您可以在字符串中找到特定的位置,并检查字符串是以特定模式开始还是以特定模式结束。例如:
~~~
// Check whether a string contains another string.
assert('Never odd or even'.contains('odd'));
// Does a string start with another string?
assert('Never odd or even'.startsWith('Never'));
// Does a string end with another string?
assert('Never odd or even'.endsWith('even'));
// Find the location of a string inside a string.
assert('Never odd or even'.indexOf('odd') == 6);
~~~
### 从字符串中提取数据
您可以分别从字符串中获取字符串中的单个字符作为字符串或整数。 确切地说,您实际上获得了单独的UTF-16代码单元; 诸如高音谱号符号('\ u {1D11E}')之类的高编号字符分别是两个代码单元。
您还可以提取子字符串或将字符串分割为一列子字符串:
~~~
// Grab a substring.
assert('Never odd or even'.substring(6, 9) == 'odd');
// Split a string using a string pattern.
var parts = 'structured web apps'.split(' ');
assert(parts.length == 3);
assert(parts[0] == 'structured');
// Get a UTF-16 code unit (as a string) by index.
assert('Never odd or even'[0] == 'N');
// Use split() with an empty string parameter to get
// a list of all characters (as Strings); good for
// iterating.
for (var char in 'hello'.split('')) {
print(char);
}
// Get all the UTF-16 code units in the string.
var codeUnitList =
'Never odd or even'.codeUnits.toList();
assert(codeUnitList[0] == 78);
~~~
### 转换为大写或小写
您可以轻松地将字符串转换为其大写和小写变体:
~~~
// Convert to uppercase.
assert('structured web apps'.toUpperCase() ==
'STRUCTURED WEB APPS');
// Convert to lowercase.
assert('STRUCTURED WEB APPS'.toLowerCase() ==
'structured web apps');
~~~
>注意:这些方法并不适用于所有语言。例如,土耳其字母表的dotless I被错误地转换。
>
### 修剪和空字符串
使用trim()删除所有前、后空白。要检查字符串是否为空(长度为零),使用isEmpty。
~~~
// Trim a string.
assert(' hello '.trim() == 'hello');
// Check whether a string is empty.
assert(''.isEmpty);
// Strings with only white space are not empty.
assert(' '.isNotEmpty);
~~~
### 构建一个字符串
要以编程方式生成字符串,可以使用StringBuffer。在调用toString()之前,StringBuffer不会生成新的字符串对象。writeAll()方法有一个可选的第二个参数,它允许您指定分隔符(在本例中是空格)。
~~~
var sb = StringBuffer();
sb
..write('Use a StringBuffer for ')
..writeAll(['efficient', 'string', 'creation'], ' ')
..write('.');
var fullString = sb.toString();
assert(fullString ==
'Use a StringBuffer for efficient string creation.');
~~~
### 正则表达式
RegExp类提供了与JavaScript正则表达式相同的功能。使用正则表达式进行字符串的高效搜索和模式匹配。
~~~
// Here's a regular expression for one or more digits.
var numbers = RegExp(r'\d+');
var allCharacters = 'llamas live fifteen to twenty years';
var someDigits = 'llamas live 15 to 20 years';
// contains() can use a regular expression.
assert(!allCharacters.contains(numbers));
assert(someDigits.contains(numbers));
// Replace every match with another string.
var exedOut = someDigits.replaceAll(numbers, 'XX');
assert(exedOut == 'llamas live XX to XX years');
~~~
您也可以直接使用RegExp类。Match类提供对正则表达式匹配的访问。
~~~
var numbers = RegExp(r'\d+');
var someDigits = 'llamas live 15 to 20 years';
// Check whether the reg exp has a match in a string.
assert(numbers.hasMatch(someDigits));
// Loop through all matches.
for (var match in numbers.allMatches(someDigits)) {
print(match.group(0)); // 15, then 20
}
~~~
### 更多信息
有关字符串操作方法的完整列表,请参阅[字符串API文档](https://api.dartlang.org/stable/dart-core/String-class.html)。还可以查看关于[StringBuffer](https://api.dartlang.org/stable/dart-core/StringBuffer-class.html)、[Pattern](https://api.dartlang.org/stable/dart-core/Pattern-class.html)、[RegExp](https://api.dartlang.org/stable/dart-core/RegExp-class.html)和[Match](https://api.dartlang.org/stable/dart-core/Match-class.html)的API文档。
## 集合(Collections)
Dart带有一个核心集合API,其中包括列表(list)、集合(sets)和映射(maps)的类。
### Lists(列表)
正如本文档list部分所示,您可以使用文字来创建和初始化列表。或者,使用列表构造函数之一。List类还定义了几种向列表中添加项和从列表中删除项的方法。
~~~
// Use a List constructor.
var vegetables = List();
// Or simply use a list literal.
var fruits = ['apples', 'oranges'];
// Add to a list.
fruits.add('kiwis');
// Add multiple items to a list.
fruits.addAll(['grapes', 'bananas']);
// Get the list length.
assert(fruits.length == 5);
// Remove a single item.
var appleIndex = fruits.indexOf('apples');
fruits.removeAt(appleIndex);
assert(fruits.length == 4);
// Remove all elements from a list.
fruits.clear();
assert(fruits.length == 0);
~~~
使用indexOf()查找列表中对象的索引:
~~~
var fruits = ['apples', 'oranges'];
// Access a list item by index.
assert(fruits[0] == 'apples');
// Find an item in a list.
assert(fruits.indexOf('apples') == 0);
~~~
使用sort()方法对列表进行排序。您可以提供一个比较两个对象的排序函数。这个排序函数必须返回< 0表示较小,0表示相同,>0表示较大。下面的示例使用compareTo(),它由Comparable定义,由String实现。
~~~
var fruits = ['bananas', 'apples', 'oranges'];
// Sort a list.
fruits.sort((a, b) => a.compareTo(b));
assert(fruits[0] == 'apples');
~~~
列表是参数化的类型,所以您可以指定列表应该包含的类型:
~~~
// This list should contain only strings.
var fruits = List<String>();
fruits.add('apples');
var fruit = fruits[0];
assert(fruit is String);
~~~
以下是错误示例:
~~~
fruits.add(5); // Error: 'int' can't be assigned to 'String'
~~~
有关方法的完整列表,请参阅[List API文档](https://api.dartlang.org/stable/dart-core/List-class.html)。
### 集合(Sets)
Dart中的集合是一组无序的独特物品集合。因为集合是无序的,所以不能通过索引(位置)获得集合的项。
~~~
var ingredients = Set();
ingredients.addAll(['gold', 'titanium', 'xenon']);
assert(ingredients.length == 3);
// Adding a duplicate item has no effect.
ingredients.add('gold');
assert(ingredients.length == 3);
// Remove an item from a set.
ingredients.remove('gold');
assert(ingredients.length == 2);
~~~
使用contains()和containsAll()来检查集合中是否有一个或多个对象:
~~~
var ingredients = Set();
ingredients.addAll(['gold', 'titanium', 'xenon']);
// Check whether an item is in the set.
assert(ingredients.contains('titanium'));
// Check whether all the items are in the set.
assert(ingredients.containsAll(['titanium', 'xenon']));
~~~
交集是一个集合,其项在另外两个集合中。
~~~
var ingredients = Set();
ingredients.addAll(['gold', 'titanium', 'xenon']);
// Create the intersection of two sets.
var nobleGases = Set.from(['xenon', 'argon']);
var intersection = ingredients.intersection(nobleGases);
assert(intersection.length == 1);
assert(intersection.contains('xenon'));
~~~
有关方法的完整列表请查看[Set API文档](https://api.dartlang.org/stable/dart-core/Set-class.html)
### 映射(Maps)
通常称为字典或散列的映射是键-值对的无序集合。映射将一个键关联到某个值,以便于检索。与JavaScript不同,Dart对象不是映射。
您可以使用简单的文字语法声明映射,也可以使用传统的构造函数:
~~~
// Maps often use strings as keys.
var hawaiianBeaches = {
'Oahu': ['Waikiki', 'Kailua', 'Waimanalo'],
'Big Island': ['Wailea Bay', 'Pololu Beach'],
'Kauai': ['Hanalei', 'Poipu']
};
// Maps can be built from a constructor.
var searchTerms = Map();
// Maps are parameterized types; you can specify what
// types the key and value should be.
var nobleGases = Map<int, String>();
~~~
使用方括号语法add、get和set映射项。使用remove()从映射中删除键及其值。
~~~
var nobleGases = {54: 'xenon'};
// Retrieve a value with a key.
assert(nobleGases[54] == 'xenon');
// Check whether a map contains a key.
assert(nobleGases.containsKey(54));
// Remove a key and its value.
nobleGases.remove(54);
assert(!nobleGases.containsKey(54));
~~~
你可以从一个map检索所有的值或所有的键:
~~~
var hawaiianBeaches = {
'Oahu': ['Waikiki', 'Kailua', 'Waimanalo'],
'Big Island': ['Wailea Bay', 'Pololu Beach'],
'Kauai': ['Hanalei', 'Poipu']
};
// Get all the keys as an unordered collection
// (an Iterable).
var keys = hawaiianBeaches.keys;
assert(keys.length == 3);
assert(Set.from(keys).contains('Oahu'));
// Get all the values as an unordered collection
// (an Iterable of Lists).
var values = hawaiianBeaches.values;
assert(values.length == 3);
assert(values.any((v) => v.contains('Waikiki')));
~~~
要检查映射是否包含某个key,请使用containsKey()。因为映射值可以为空,所以不能简单地依赖于获取键的值并检查是否为空来确定键的存在。
~~~
var hawaiianBeaches = {
'Oahu': ['Waikiki', 'Kailua', 'Waimanalo'],
'Big Island': ['Wailea Bay', 'Pololu Beach'],
'Kauai': ['Hanalei', 'Poipu']
};
assert(hawaiianBeaches.containsKey('Oahu'));
assert(!hawaiianBeaches.containsKey('Florida'));
~~~
有关方法的完整列表请查看 [Map API文档](https://api.dartlang.org/stable/dart-core/Map-class.html)
### 集合类型的共同方法
列表、集合和映射共享许多集合中的公共功能。其中一些常见功能是由Iterable类定义的,该类列出并设置实现。
>注意:尽管Map没有实现Iterable,但是您可以使用Map键和值属性从它获得迭代。
>
使用isEmpty或isNotEmpty检查列表、集合或映射是否有项:
~~~
var coffees = [];
var teas = ['green', 'black', 'chamomile', 'earl grey'];
assert(coffees.isEmpty);
assert(teas.isNotEmpty);
~~~
要对列表、集合或映射中的每个项执行函数,可以使用forEach():
~~~
var teas = ['green', 'black', 'chamomile', 'earl grey'];
teas.forEach((tea) => print('I drink $tea'));
~~~
在映射上调用forEach()时,函数必须接受两个参数(键和值):
~~~
hawaiianBeaches.forEach((k, v) {
print('I want to visit $k and swim at $v');
// I want to visit Oahu and swim at
// [Waikiki, Kailua, Waimanalo], etc.
});
~~~
Iterables提供map()方法,它可以在一个对象中提供所有结果:
~~~
var teas = ['green', 'black', 'chamomile', 'earl grey'];
var loudTeas = teas.map((tea) => tea.toUpperCase());
loudTeas.forEach(print);
~~~
>注意:map()返回的对象是一个可迭代的、延迟求值的对象:直到从返回的对象请求项时才调用函数。
>
强制在每个项上立即调用函数,使用map().toList()或map().toSet():
~~~
var loudTeas =
teas.map((tea) => tea.toUpperCase()).toList();
~~~
使用迭代器的where()方法获得所有与条件匹配的项。使用迭代器的any()和every()方法检查某些或所有项是否匹配一个条件。
~~~
var teas = ['green', 'black', 'chamomile', 'earl grey'];
// Chamomile is not caffeinated.
bool isDecaffeinated(String teaName) =>
teaName == 'chamomile';
// Use where() to find only the items that return true
// from the provided function.
var decaffeinatedTeas =
teas.where((tea) => isDecaffeinated(tea));
// or teas.where(isDecaffeinated)
// Use any() to check whether at least one item in the
// collection satisfies a condition.
assert(teas.any(isDecaffeinated));
// Use every() to check whether all the items in a
// collection satisfy a condition.
assert(!teas.every(isDecaffeinated));
~~~
要获得完整的方法列表,请参考[Iterable API文档](https://api.dartlang.org/stable/dart-core/Iterable-class.html)以及[List](https://api.dartlang.org/stable/dart-core/List-class.html)、[Set](https://api.dartlang.org/stable/dart-core/Set-class.html)和[Map](https://api.dartlang.org/stable/dart-core/Map-class.html)文档。
## URIs
Uri类提供了对字符串进行编码和解码的函数,以便在Uri中使用(您可能知道url)。这些函数处理uri特有的字符,例如&和=。Uri类还解析并公开Uri主机、端口、 协议等的组件。
### 编码和解码完整的uri
要对URI中具有特殊含义的字符(例如/、:、&、#)进行编码和解码,请使用encodeFull()和decodeFull()方法。这些方法适用于编码或解码完整的URI,保留完整的特殊URI字符。
~~~
var uri = 'http://example.org/api?foo=some message';
var encoded = Uri.encodeFull(uri);
assert(encoded ==
'http://example.org/api?foo=some%20message');
var decoded = Uri.decodeFull(encoded);
assert(uri == decoded);
~~~
注意,只有some和message中间的空格被编码
### 对URI组件进行编码和解码
要对URI中具有特殊含义的所有字符串字符进行编码和解码,包括(但不限于)/、&和:,请使用encodeComponent()和decodeComponent()方法。
~~~
var uri = 'http://example.org/api?foo=some message';
var encoded = Uri.encodeComponent(uri);
assert(encoded ==
'http%3A%2F%2Fexample.org%2Fapi%3Ffoo%3Dsome%20message');
var decoded = Uri.decodeComponent(encoded);
assert(uri == decoded);
~~~
注意每个特殊字符是如何编码的。例如,/被编码为%2F。
### 解析uri
如果您有一个Uri对象或一个Uri字符串,您可以使用Uri字段(如path)获得它的部分。要从字符串创建Uri,使用parse()静态方法:
~~~
var uri =
Uri.parse('http://example.org:8080/foo/bar#frag');
assert(uri.scheme == 'http');
assert(uri.host == 'example.org');
assert(uri.path == '/foo/bar');
assert(uri.fragment == 'frag');
assert(uri.origin == 'http://example.org:8080');
~~~
查看[Uri API文档](https://api.dartlang.org/stable/dart-core/Uri-class.html)可以获得更多的Uri组件。
### 构建uri
您可以使用Uri()构造函数从各个部分构建一个URI:
~~~
var uri = Uri(
scheme: 'http',
host: 'example.org',
path: '/foo/bar',
fragment: 'frag');
assert(
uri.toString() == 'http://example.org/foo/bar#frag');
~~~
## 日期和时间
DateTime对象是一个时间点。时区不是UTC就是当地时区。
你可以使用几个构造函数来创建DateTime对象:
~~~
// Get the current date and time.
var now = DateTime.now();
// Create a new DateTime with the local time zone.
var y2k = DateTime(2000); // January 1, 2000
// Specify the month and day.
y2k = DateTime(2000, 1, 2); // January 2, 2000
// Specify the date as a UTC time.
y2k = DateTime.utc(2000); // 1/1/2000, UTC
// Specify a date and time in ms since the Unix epoch.
y2k = DateTime.fromMillisecondsSinceEpoch(946684800000,
isUtc: true);
// Parse an ISO 8601 date.
y2k = DateTime.parse('2000-01-01T00:00:00Z');
~~~
日期的millisecondsSinceEpoch属性返回自“Unix epoch”(1970年1月1日,UTC)以来的毫秒数:
~~~
// 1/1/2000, UTC
var y2k = DateTime.utc(2000);
assert(y2k.millisecondsSinceEpoch == 946684800000);
// 1/1/1970, UTC
var unixEpoch = DateTime.utc(1970);
assert(unixEpoch.millisecondsSinceEpoch == 0);
~~~
使用Duration类来计算两个日期之间的差值,并将一个日期向前或向后移动:
~~~
var y2k = DateTime.utc(2000);
// Add one year.
var y2001 = y2k.add(Duration(days: 366));
assert(y2001.year == 2001);
// Subtract 30 days.
var december2000 = y2001.subtract(Duration(days: 30));
assert(december2000.year == 2000);
assert(december2000.month == 12);
// Calculate the difference between two dates.
// Returns a Duration object.
var duration = y2001.difference(y2k);
assert(duration.inDays == 366); // y2k was a leap year.
~~~
>警告:使用持续时间以天为单位来移动日期时间是有问题的,因为时钟会改变(例如,夏令时)。如果你必须换班,请使用UTC日期。
>
有关[DateTime](https://api.dartlang.org/stable/dart-core/DateTime-class.html) 和[Duration](https://api.dartlang.org/stable/dart-core/Duration-class.html)的完整方法列表,请参阅API文档。
## 实用程序类
核心库包含各种实用程序类,用于排序、映射值和迭代。
### 比较对象
实现Comparable接口,以指示一个对象可以与另一个对象进行比较,通常用于排序。compareTo()方法返回< 0对于小的,0对于相等的,大的返回>0。
~~~
class Line implements Comparable<Line> {
final int length;
const Line(this.length);
@override
int compareTo(Line other) => length - other.length;
}
void main() {
var short = const Line(1);
var long = const Line(100);
assert(short.compareTo(long) < 0);
}
~~~
### 实现map键
Dart中的每个对象自动地提供了一个整数哈希码,因此可以作为映射中的键。但是,您可以重写hashCode getter来生成自定义哈希代码。如果这样做,您可能还想重写==操作符。相等的对象(via ==)必须具有相同的哈希码。哈希代码不一定是唯一的,但它应该是分布良好的。
~~~
class Person {
final String firstName, lastName;
Person(this.firstName, this.lastName);
// Override hashCode using strategy from Effective Java,
// Chapter 11.
@override
int get hashCode {
int result = 17;
result = 37 * result + firstName.hashCode;
result = 37 * result + lastName.hashCode;
return result;
}
// You should generally implement operator == if you
// override hashCode.
@override
bool operator ==(dynamic other) {
if (other is! Person) return false;
Person person = other;
return (person.firstName == firstName &&
person.lastName == lastName);
}
}
void main() {
var p1 = Person('Bob', 'Smith');
var p2 = Person('Bob', 'Smith');
var p3 = 'not a person';
assert(p1.hashCode == p2.hashCode);
assert(p1 == p2);
assert(p1 != p3);
}
~~~
### 迭代
Iterable类和Iterator类支持for-in循环。无论何时,只要您创建了一个类,可以为for-in循环提供迭代器,就可以扩展(如果可能的话)或实现Iterable。实现迭代器来定义实际的迭代能力。
~~~
class Process {
// Represents a process...
}
class ProcessIterator implements Iterator<Process> {
@override
Process get current => ...
@override
bool moveNext() => ...
}
// A mythical class that lets you iterate through all
// processes. Extends a subclass of [Iterable].
class Processes extends IterableBase<Process> {
@override
final Iterator<Process> iterator = ProcessIterator();
}
void main() {
// Iterable objects can be used with for-in.
for (var process in Processes()) {
// Do something with the process.
}
}
~~~
## 异常
Dart核心库定义了许多常见的异常和错误。异常被认为是可以提前计划和捕捉的条件。错误是您没有预料到或计划的情况。
一些最常见的错误是:
[NoSuchMethodError](https://api.dartlang.org/stable/dart-core/NoSuchMethodError-class.html)
当接收对象(可能为空)没有实现方法时抛出。
[ArgumentError](https://api.dartlang.org/stable/dart-core/ArgumentError-class.html)
可以由遇到意外参数的方法抛出。
抛出特定于应用程序的异常是表示发生了错误的常见方法。您可以通过实现异常接口来定义自定义异常:
~~~
class FooException implements Exception {
final String msg;
const FooException([this.msg]);
@override
String toString() => msg ?? 'FooException';
}
~~~
更多信息请查看 [Exceptions](https://www.dartlang.org/guides/libraries/library-tour#exceptions) 和 [Exception API 文档](https://api.dartlang.org/stable/dart-core/Exception-class.html)。
# dart:async - 异步编程
异步编程通常使用回调函数,但是Dart提供了其他方法:[Future](https://api.dartlang.org/stable/dart-async/Future-class.html)对象和[Stream](https://api.dartlang.org/stable/dart-async/Stream-class.html)对象。Future就像对未来某个时刻的结果的承诺。Stream是一种获取值序列(如事件)的方法。dart:async库([API参考](https://api.dartlang.org/stable/dart-async/dart-async-library.html))中包含了Future, Stream和更多内容。
>注意:您并不总是需要直接使用Future api或Stream api。Dart语言支持使用诸如async和await等关键字进行异步编码。有关详细信息,请参阅语本文档中的[异步支持](https://www.kancloud.cn/marswill/dark2_document/709109)。
>
dart:async库在web应用程序和命令行应用程序中都能工作。要使用它,导入dart:async:
~~~
import 'dart:async';
~~~
## Future
Future对象出现在Dart库中,通常是异步方法返回的对象。当Future完成时,它的值就可以使用了。
### 使用await
在直接使用Future API之前,请考虑使用await。使用await表达式的代码比使用Future API的代码更容易理解。
考虑下面的函数。它使用Future的then()方法在一行中执行三个异步函数,等待每个函数完成后再执行下一个。
~~~
runUsingFuture() {
// ...
findEntryPoint().then((entryPoint) {
return runExecutable(entryPoint, args);
}).then(flushThenExit);
}
~~~
具有await表达式的等效代码看起来更像同步代码:
~~~
runUsingAsyncAwait() async {
// ...
var entryPoint = await findEntryPoint();
var exitCode = await runExecutable(entryPoint, args);
await flushThenExit(exitCode);
}
~~~
异步函数可以捕获Futures中的异常。例如:
~~~
var entryPoint = await findEntryPoint();
try {
var exitCode = await runExecutable(entryPoint, args);
await flushThenExit(exitCode);
} catch (e) {
// Handle the error...
}
~~~
>重要提示:异步函数返回Future。如果您不希望函数返回Future,那么使用不同的解决方案。例如,您可以从您的函数调用一个异步函数。
>
有关使用await和相关Dart语言特性的更多信息,请参阅[异步支持](https://www.dartlang.org/guides/language/language-tour#asynchrony-support)。
### 基本使用
您可以使用then()来调度Future完成时运行的代码。例如,HttpRequest.getString()返回一个Future,因为HTTP请求可能需要一段时间。使用then()可以让您在Future完成并且承诺的字符串值可用时运行一些代码:
~~~
HttpRequest.getString(url).then((String result) {
print(result);
});
~~~
使用catchError()来处理Future对象可能抛出的任何错误或异常。
~~~
HttpRequest.getString(url).then((String result) {
print(result);
}).catchError((e) {
// Handle or ignore the error.
});
~~~
then().catchError()模式是try-catch的异步版本。
>重要提示:一定要在then()的结果上调用catchError()——而不是在原始Future的结果上。否则,catchError()只能从原始Future的计算中处理错误,而不能从then()注册的处理程序处理错误。
>
### 链接多个异步方法
then()方法返回一个Future,提供了一种有用的方式来以特定的顺序运行多个异步函数。如果用then()注册的回调返回一个Future,那么then()返回一个等效的Future。如果回调返回任何其他类型的值,那么then()将创建一个新的Future,并使用该值完成操作。
~~~
Future result = costlyQuery(url);
result
.then((value) => expensiveWork(value))
.then((_) => lengthyComputation())
.then((_) => print('Done!'))
.catchError((exception) {
/* Handle exception... */
});
~~~
在前面的例子中,方法按照以下顺序运行:
1. costlyQuery()
2. expensiveWork()
3. lengthyComputation()
下面是使用await编写的相同代码:
~~~
try {
final value = await costlyQuery(url);
await expensiveWork(value);
await lengthyComputation();
print('Done!');
} catch (e) {
/* Handle exception... */
}
~~~
### 等待多个Future
有时您的算法需要调用许多异步函数,并等待它们全部完成后再继续。使用Future.wait()静态方法管理多个期货,并等待它们完成:
~~~
Future deleteLotsOfFiles() async => ...
Future copyLotsOfFiles() async => ...
Future checksumLotsOfOtherFiles() async => ...
await Future.wait([
deleteLotsOfFiles(),
copyLotsOfFiles(),
checksumLotsOfOtherFiles(),
]);
print('Done with all the long steps!');
~~~
## Stream
流对象出现在Dart api中,表示数据序列。例如,HTML事件(如按钮单击)是使用流传递的。您还可以将文件读取为流。
### 使用异步for循环
有时候,您可以使用异步for循环(wait for),而不是使用流API。
考虑下面的函数。它使用Stream的listen()方法订阅一个文件列表,传入一个搜索每个文件或目录的函数文字。
~~~
void main(List<String> arguments) {
// ...
FileSystemEntity.isDirectory(searchPath).then((isDir) {
if (isDir) {
final startingDir = Directory(searchPath);
startingDir
.list(
recursive: argResults[recursive],
followLinks: argResults[followLinks])
.listen((entity) {
if (entity is File) {
searchFile(entity, searchTerms);
}
});
} else {
searchFile(File(searchPath), searchTerms);
}
});
}
~~~
带有await表达式的等价代码,包括异步for循环(await for),看起来更像同步代码:
~~~
Future main(List<String> arguments) async {
// ...
if (await FileSystemEntity.isDirectory(searchPath)) {
final startingDir = Directory(searchPath);
await for (var entity in startingDir.list(
recursive: argResults[recursive],
followLinks: argResults[followLinks])) {
if (entity is File) {
searchFile(entity, searchTerms);
}
}
} else {
searchFile(File(searchPath), searchTerms);
}
}
~~~
>重要提示:在使用await for之前,确保它使代码更清晰,并且您确实希望等待流的所有结果。例如,对于DOM事件侦听器,通常不应该使用await For,因为DOM发送无穷无尽的事件流。如果您使用await for在一行中注册两个DOM事件侦听器,那么第二类事件永远不会被处理。
>
有关使用await和相关Dart语言特性的更多信息,请参阅[异步支持](https://www.dartlang.org/guides/language/language-tour#asynchrony-support)。
### 监听流数据
要在每个值到达时获得它,可以使用await()方法对流使用或使用listen()方法订阅:
~~~
// Find a button by ID and add an event handler.
querySelector('#submitInfo').onClick.listen((e) {
// When the button is clicked, it runs this code.
submitData();
});
~~~
在本例中,onClick属性是“submitInfo”按钮提供的流对象。
如果只关心一个事件,那么可以使用属性first、last或single来获得它。要在处理事件之前测试它,可以使用诸如firstWhere()、lastWhere()或singleWhere()之类的方法。
如果关心事件的子集,可以使用诸如skip()、skipWhile()、take()、takeWhile()和where()等方法。
### 改变流数据
通常,您需要在使用流数据之前更改其格式。使用transform()方法生成具有不同类型数据的流:
~~~
var lines = inputStream
.transform(utf8.decoder)
.transform(LineSplitter());
~~~
这个例子使用了两个转换器。首先,它使用utf8.decoder将整数流转换为字符串流。然后,它使用linesp才将字符串流转换为单独的行流。这些转换器来自dart:convert库(参见[dart:convert部分](https://www.dartlang.org/guides/libraries/library-tour#dartconvert---decoding-and-encoding-json-utf-8-and-more))。
### 处理错误和完成
如何指定错误和完成处理代码取决于是使用异步for循环(wait for)还是流API。
如果使用异步for循环,则使用try-catch处理错误。在流关闭后执行的代码在异步for循环之后执行。
~~~
Future readFileAwaitFor() async {
var config = File('config.txt');
Stream<List<int>> inputStream = config.openRead();
var lines = inputStream
.transform(utf8.decoder)
.transform(LineSplitter());
try {
await for (var line in lines) {
print('Got ${line.length} characters from stream');
}
print('file is now closed');
} catch (e) {
print(e);
}
}
~~~
如果使用流API,则通过注册onError侦听器来处理错误。通过注册onDone侦听器,在流关闭后运行代码。
~~~
var config = File('config.txt');
Stream<List<int>> inputStream = config.openRead();
inputStream
.transform(utf8.decoder)
.transform(LineSplitter())
.listen((String line) {
print('Got ${line.length} characters from stream');
}, onDone: () {
print('file is now closed');
}, onError: (e) {
print(e);
});
~~~
### 更多信息
有关在命令行应用程序中使用Future和Stream的一些示例,请参阅[dart:io的引导](https://www.dartlang.org/dart-vm/io-library-tour)。也看这些文章和教程:
* [Asynchronous Programming: Futures](https://www.dartlang.org/tutorials/language/futures)
* [Futures and Error Handling](https://www.dartlang.org/guides/libraries/futures-error-handling)
* [The Event Loop and Dart](https://webdev.dartlang.org/articles/performance/event-loop)
* [Asynchronous Programming: Streams](https://www.dartlang.org/tutorials/language/streams)
* [Creating Streams in Dart](https://www.dartlang.org/articles/libraries/creating-streams)
# dart:math - math and random
dart:math 库 ([API 参考](https://api.dartlang.org/stable/dart-math/dart-math-library.html))提供了常见的功能,如正弦和余弦,最大值和最小值,以及常量,如pi和e。Math 库中的大多数功能是作为顶级函数实现的。
要在应用程序中使用这个库,导入dart:math。
~~~
import 'dart:math';
~~~
## 三角函数
数学库提供基本三角函数:
~~~
// Cosine
assert(cos(pi) == -1.0);
// Sine
var degrees = 30;
var radians = degrees * (pi / 180);
// radians is now 0.52359.
var sinOf30degrees = sin(radians);
// sin 30° = 0.5
assert((sinOf30degrees - 0.5).abs() < 0.01);
~~~
>注意:这些函数使用弧度,而不是角度!
>
## 最大和最小值
数学库提供max()和min()方法:
~~~
assert(max(1, 1000) == 1000);
assert(min(1, -1000) == -1000);
~~~
## 数学常量
在数学库中找到你最喜欢的常量- pi, e,以及更多:
~~~
// See the Math library for additional constants.
print(e); // 2.718281828459045
print(pi); // 3.141592653589793
print(sqrt2); // 1.4142135623730951
~~~
## 随机数
用Random类生成随机数。您可以选择向随机构造函数提供种子。
~~~
var random = Random();
random.nextDouble(); // Between 0.0 and 1.0: [0, 1)
random.nextInt(10); // Between 0 and 9.
~~~
你甚至可以产生随机布尔:
~~~
var random = Random();
random.nextBool(); // true or false
~~~
## 更多信息
有关方法的完整列表,请参阅[Math API文档](https://api.dartlang.org/stable/dart-math/dart-math-library.html)。还可以查看有关[num](https://api.dartlang.org/stable/dart-core/num-class.html)、[int](https://api.dartlang.org/stable/dart-core/int-class.html)和[double](https://api.dartlang.org/stable/dart-core/double-class.html)的API文档。
# dart:convert - 解码和编码JSON、UTF-8等等
dart:convert库([API reference](https://api.dartlang.org/stable/dart-convert/dart-convert-library.html))为JSON和UTF-8提供了转换器,并且支持创建额外的转换器。[JSON](https://www.json.org/)是一种表示结构化对象和集合的简单文本格式。[UTF-8](https://en.wikipedia.org/wiki/UTF-8)是一种常见的变宽编码,可以表示Unicode字符集中的每个字符。
dart:convert 库可以在web应用程序和命令行应用程序中工作。要使用它,导入dart:convert。
~~~
import 'dart:convert';
~~~
## 编码接吗JSON
用jsonDecode()将json编码的字符串解码为Dart对象:
~~~
// NOTE: Be sure to use double quotes ("),
// not single quotes ('), inside the JSON string.
// This string is JSON, not Dart.
var jsonString = '''
[
{"score": 40},
{"score": 80}
]
''';
var scores = jsonDecode(jsonString);
assert(scores is List);
var firstScore = scores[0];
assert(firstScore is Map);
assert(firstScore['score'] == 40);
~~~
用jsonEncode()将受支持的Dart对象编码为json格式的字符串:
~~~
var scores = [
{'score': 40},
{'score': 80},
{'score': 100, 'overtime': true, 'special_guest': null}
];
var jsonText = jsonEncode(scores);
assert(jsonText ==
'[{"score":40},{"score":80},'
'{"score":100,"overtime":true,'
'"special_guest":null}]');
~~~
只有int、double、String、bool、null、List或Map(带字符串键)类型的对象可以直接编码为JSON。列表和映射对象是递归编码的。
对于不能直接编码的对象,有两个编码选项。第一个是使用第二个参数调用encode():一个返回可直接编码对象的函数。第二个选项是省略第二个参数,在这种情况下,编码器调用对象的toJson()方法。
## 解码和编码UTF-8字符
使用utf8.decode()将utf8编码的字节解码到Dart字符串:
~~~
List<int> utf8Bytes = [
0xc3, 0x8e, 0xc3, 0xb1, 0xc5, 0xa3, 0xc3, 0xa9,
0x72, 0xc3, 0xb1, 0xc3, 0xa5, 0xc5, 0xa3, 0xc3,
0xae, 0xc3, 0xb6, 0xc3, 0xb1, 0xc3, 0xa5, 0xc4,
0xbc, 0xc3, 0xae, 0xc5, 0xbe, 0xc3, 0xa5, 0xc5,
0xa3, 0xc3, 0xae, 0xe1, 0xbb, 0x9d, 0xc3, 0xb1
];
var funnyWord = utf8.decode(utf8Bytes);
assert(funnyWord == 'Îñţérñåţîöñåļîžåţîờñ');
~~~
要将UTF-8字符流转换为Dart字符串,请将utf8.decoder指定为stream transform()方法:
~~~
var lines = inputStream
.transform(utf8.decoder)
.transform(LineSplitter());
try {
await for (var line in lines) {
print('Got ${line.length} characters from stream');
}
print('file is now closed');
} catch (e) {
print(e);
}
~~~
使用utf8.encode()将Dart字符串编码为utf8编码字节的列表:
~~~
List<int> encoded = utf8.encode('Îñţérñåţîöñåļîžåţîờñ');
assert(encoded.length == utf8Bytes.length);
for (int i = 0; i < encoded.length; i++) {
assert(encoded[i] == utf8Bytes[i]);
}
~~~
## 其他功能
dart:convert库也有ASCII和ISO-8859-1 (Latin1)转换器。有关dart:convert库的详细信息,请参阅[API文档](https://api.dartlang.org/stable/dart-convert/dart-convert-library.html)。
# 总结
这个页面向您介绍了Dart内置库中最常用的功能。然而,它并没有覆盖所有内置库。您可能想了解的其他内容包括[dart:collection](https://api.dartlang.org/stable/dart-collection/dart-collection-library.html)和[dart:typed_data](https://api.dartlang.org/stable/dart-typed_data/dart-typed_data-library.html),以及特定于平台的libaries(如[dart web开发库](https://webdev.dartlang.org/guides/web-programming)和[Flutter库](https://docs.flutter.io/))。
您可以使用[pub工具](https://www.dartlang.org/tools/pub)获得更多的库。[collection](https://pub.dartlang.org/packages/collection), [crypto](https://pub.dartlang.org/packages/crypto), [http](https://pub.dartlang.org/packages/http), [intl](https://pub.dartlang.org/packages/intl), 和 [test](https://pub.dartlang.org/packages/test)库只是使用pub安装内容的一个示例。
要了解更多关于Dart语言的信息,请参见本文档其他章节。