本教程展示了如何使用下列庫的主要功能,這些庫包含在所有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等關鍵字進行異步編碼。有關詳細信息,請參閱語本文檔中的[異步支持](http://www.hmoore.net/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語言的信息,請參見本文檔其他章節。