本教程展示了如何使用下列库的主要功能,这些库包含在所有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语言的信息,请参见本文档其他章节。