前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Dart的语法详解系列篇(四)-- 泛型、异步、库等有关详解九、泛型(Generics)十、库和可见性十一、异步支持十二、Isolates十三、生成器(Generators)十四、类型定义十五、元数据

Dart的语法详解系列篇(四)-- 泛型、异步、库等有关详解九、泛型(Generics)十、库和可见性十一、异步支持十二、Isolates十三、生成器(Generators)十四、类型定义十五、元数据

作者头像
AWeiLoveAndroid
发布2019-01-03 11:11:20
3.6K1
发布2019-01-03 11:11:20
举报

上一篇主要讲了Dart的类与函数,由于内容有太多,我就把剩下的内容分开写一篇文章。 这一篇我们讲Dart的泛型、异步、库等有关详解,内容较多,希望大家可以耐心看完。我也是花了很长时间研究的。喜欢的就点个赞,打个赏吧。 感谢大家支持。


九、泛型(Generics)

如果您查看基本数组类型的API文档 List,您会看到该类型实际上是List<E>。<...>表示法将List标记为 泛型(或参数化)类型 - 具有正式类型参数的类型。按照惯例,大多数类型变量都有单字母名称,例如E,T,S,K和V.

(一)为什么使用泛型?

类型安全通常需要泛型,但它们比仅允许代码运行有更多好处:

1).正确指定泛型类型可以生成更好的代码。

如果您希望列表只包含字符串,则可以将其声明为List<String>(将其读作“字符串列表”)。这样一来,工具可以检测到将非字符串分配给列表可能是一个错误。 例子:

代码语言:javascript
复制
var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
// 报错 The argument type 'int' can't be assigned to the parameter type 'String'.
names.add(42); 
2).您可以使用泛型来减少代码重复。

泛型允许您在多种类型之间共享单个接口和实现,同时仍然利用静态分析。 例如:创建了一个用于缓存对象的接口:

代码语言:javascript
复制
abstract class ObjectCache {
  Object getByKey(String key);
  void setByKey(String key, Object value);
}

您发现需要此接口针对字符串的做一个缓存,因此您需要创建另一个接口:

代码语言:javascript
复制
abstract class StringCache {
  String getByKey(String key);
  void setByKey(String key, String value);
}

如果还有其他更改,就要写很多接口。 泛型可以省去创建所有这些接口的麻烦。你可以创建一个带有类型参数的接口。 示例如下:T是一个占位符,您可以将其视为开发人员稍后定义的类型。

代码语言:javascript
复制
abstract class Cache<T> {
  T getByKey(String key);
  void setByKey(String key, T value);
}

(二)使用集合文字

list和map文字可以参数化。参数化文字就像你已经看到的文字一样,除了你在开始括号之前添加 <type>(对于list)或 <keyType, valueType>(对于map)。 以下是使用类型文字(typed literals)的示例:

代码语言:javascript
复制
var numbers = <String>['11', '22', '33'];
var pages = <String, String>{
  'index.html': 'Homepage',
  'store.html': 'Store',
  'mine.html': 'Mine'
};

(三)使用带有构造函数的参数化类型

要在使用构造函数时指定一个或多个类型,请将类型放在类名称后面的尖括号<...>中。例如:

代码语言:javascript
复制
var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
var nameSet = Set<String>.from(names);

以下代码创建一个具有整数的key和View类型的value的map:

代码语言:javascript
复制
var views = Map<int, View>();

(四)泛型集合及其包含的类型

Dart的泛型类型是具体的。也就说,它们在运行时会会携带类型信息。示例如下:(相反,Java中的泛型使用擦除,这意味着在运行时删除泛型类型参数。在Java中,您可以测试对象是否为List,但您无法测试它是否是List<String>。)

代码语言:javascript
复制
var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
print(names is List<String>); // true
print(names.runtimeType); // List<String>

(五)限制参数类型

实现泛型类型时,您可能希望限制其参数的类型。你可以在<>里面使用extends。 例如:

代码语言:javascript
复制
abstract class SomeBaseClass {
    // 其他操作
}

class Foo<T extends SomeBaseClass> {
  String toString() {
      return "Instance of Foo<$T>";
  }
}

class Extender extends SomeBaseClass {
     //其他操作
}

现在可以使用SomeBaseClass或它的任何子类作为泛型参数。

例如:

代码语言:javascript
复制
void main() {
    var someBaseClassFoo = Foo<SomeBaseClass>();
    var extenderFoo = Foo<Extender>();
    print(someBaseClassFoo.toString());// Instance of Foo<SomeBaseClass>
    print(extenderFoo.toString());// Instance of Foo<SomeBaseClass>
}

也可以不指定泛型参数。 例如:

代码语言:javascript
复制
var foo = Foo();
//等同于print(foo.toString());
print(foo);// Instance of Foo<SomeBaseClass>

如果指定任何非SomeBaseClass类型会导致错误。 例如:var foo = Foo<Object>;

(六)使用泛型方法

新版本的Dart的泛型方法,允许在方法和函数上使用类型参数。(但它同样适用于实例方法,静态方法,顶级函数,本地函数甚至lambda表达式。) 例如:

代码语言:javascript
复制
T first<T>(List<T> data) {
      // 做一些初始工作或错误检查...
      T tmp = data[0];
  // 做一些额外的检查或处理...
  return tmp;
}

在first(<T>)上的的泛型类型参数,允许你在以下几个地方使用类型参数T:

  • 1). 在函数的返回类型(T)中
  • 2). 在参数类型(List<T>)中
  • 3). 在局部变量的类型(T tmp)

泛型方法可以声明类方法(实例和静态)以相同的方式获取泛型参数。

代码语言:javascript
复制
class  C {
   static  int f < S,T >(int x)= >  3 ;
  int m < S,T >(int x)= >  3 ;
}

泛型方法也适用于函数类型参数,本地函数和函数表达式。 // 将泛型方法作为参数[callback]。

代码语言:javascript
复制
void  functionTypedParameter(T callback <T>(T thing)){}

// 声明一个本地泛型函数`本身`。
void  localFunction(){
   T itself<T>(T thing) => thing;
}

// 将泛型函数表达式绑定到局部变量。
void functionExpression(){
   var lambda =  <T>(T thing) => thing;
}

十、库和可见性

该import和library指令可以帮助您创建一个模块化的,可共享的代码库。库不仅提供API,还是隐私单元(以下划线(_)开头的标识符仅在库内可见)。每个Dart应用程序都是一个库,即使它不使用library指令。可以使用包来分发库。

(一)使用库

使用import指定一个库中的命名空间如何在另一个库汇总使用。 例如,Dart Web应用程序通常使用dart:html 库,它们可以像这样导入: import 'dart:html'; 对于内置库,URI具有特殊dart: 方案(scheme)。对于其他库,您可以使用文件系统路径或package: 方案(scheme),这个是由包管理器(如pub工具)提供的库。 例如: import 'libs/mylib.dart';

(二)指定库前缀

如果导入两个具有冲突标识符的库,则可以为一个或两个库指定前缀。例如,如果test2.dart和test3.dart都有一个hello()函数,那么直接导入这两个文件会有冲突,这种情况下我们可以使用as关键字给库指定一个前缀:

test2.dart代码如下:

代码语言:javascript
复制
void hello() {
  print('test2.dart : hello()函数');
}

test3.dart代码如下:
void hello(){
  print('test3.dart : hello()函数');
}

现在要在test1.dart中导入这两个文件:
// 这样写会报错
// import 'test2.dart';
// import 'test3.dart';
    
  // 正确写法:
import 'test2.dart';
// 给导入的库指定一个前缀 方便识别
import 'test3.dart' as test3;
  
  调用方式:
  void main(){
    hello();//test2.dart : hello()函数
    test3.hello();//test3.dart : hello()函数
}

导入库也可以使用相对路径。例如:lib/demo1/a.dart, lib/demo2/b.dart这两个文件。现在b.dart这个文件需要引用a.dart,可以使用import '../demo1/a.dart'导入。

(三)仅导入库的一部分

如果只想使用库的一部分,则可以有选择地导入库,可以使用show或者hide关键字。例如:show表示仅导入当前库,hide表示除了当前库之外全部导入。

代码语言:javascript
复制
// 仅导入mylib.dart里面的test2函数
// import 'libs/mylib.dart' show test2;
// 刚好和show相反 除了test2函数之外  其它的都导入
import 'libs/mylib.dart' hide test2;
//我们想导入mylib库,但是不想用里面的otherLib这个库 可以这样写
// import 'libs/mylib.dart' hide otherLib;

(四)懒加载一个库

延迟加载(也称为延迟加载)允许应用程序根据需要加载库,如果需要的话。以下是您可能使用延迟加载的一些情况:

  • 1).减少应用程序的初始启动时间。
  • 2).例如,执行A/B测试 - 尝试算法的替代实现。
  • 3).加载很少使用的功能,例如可选的屏幕和对话框。 要延迟加载库,必须先使用deferred as它导入一个库。当我们import一个库的时候,如果使用了as 不能同时使用deferred as 例如:
代码语言:javascript
复制
// import 'libs/mylib.dart'; // 不能同时使用
import 'libs/mylib.dart' deferred as tests;

当您需要库时,使用库的标识符调用loadLibrary()。 例如(注意导包:import 'dart:async';):

代码语言:javascript
复制
Future hello() async {
  await tests.loadLibrary();
  tests.test2();
}
// 然后再去使用:
void main(){
  hello(); // 结果是: mylib.dart:test2()函数
}

在上述的代码中,await关键字暂停执行,直到库被加载。 您可以在一个库上调用loadLibrary()多次,而不会出现问题。该库只加载一次。

使用延迟加载时请记住以下内容:

  • 1).延迟库的常量不是导入文件中的常量。请记住,在加载延迟库之前,这些常量不存在。
  • 2).您不能在导入文件中使用延迟库中的类型。相反,请考虑将接口类型移动到由延迟库和导入文件导入的*库。
  • 3).Dart隐式插入loadLibrary()到你使用deferred as namespace定义的命名空间。loadLibrary()函数返回Future。

(五)库的拆分

【说明】dart官网不推荐使用part ,这个仅作为了解。 使用part指令,可以将库拆分为多个Dart文件。part of表示隶属于某个库的一部分。 注意事项:

  • 1.不能同时使用library和part of,它们都用于指定属于库的内容。
代码语言:javascript
复制
// library testlib2; 这个不能和part of同时使用 会报错
// part of 表示这个库是testlib库的一部分
part of testlib1;
    1. B库是A库的一部分,在B库里面声明:part of A库名称 例如:在testlib2.dart里面声明 part of testlib1; 表示testlib2这个库是testlib库的yi部分。
    1. 如果B库声明A库的一部分,同时A库也想声明它的一部分是B库,正确写法:B库声明part of A库名称,然后A库声明part 'B库的路径' , 同时,如果B库没有声明,那么在A库里面使用part指令会报错。 testlib1.dart内容:
代码语言:javascript
复制
// 第1个库:
library testlib1;
// 可以不写
part 'testlib2.dart';
void run() {
  print('testlib1库 : run()函数');
}

testlib2.dart内容:

代码语言:javascript
复制
part of testlib1;
class testLib2 {}
void start() {
  print('testlib2库 : start()函数');
}
    1. B库声明了part of A库名称,A库可以省去声明part 'B库的路径'
代码语言:javascript
复制
// 第1个库:
library testlib1;
// 可以不写
part 'testlib2.dart';

(六)库的自动导入

在A库中使用export关键字引入B库,当我们使用A库的时候,会自动引入B库,也就是说我们导入了A库,就可以使用B库了。 mylib.dart内容为:

代码语言:javascript
复制
// 这是一个库 命名为mylib
library mylib;
// 希望使用mylib的时候 自动使用otherlib.dart  可以使用export关键字引入其他库
export 'otherlib.dart';
// 导入otherlib2.dart
export 'otherlib2.dart';

class MyLib {
  void test() {
    print('mylib.dart: MyLib : test()函数');
  }
}

void test2() {
  print('mylib.dart: test2()函数');
}

otherlib.dart库内容为:

代码语言:javascript
复制
// otherlib库
library otherlib;
class otherLib {}
void test() {
  print('otherLib库 : test()函数');
}

otherlib2.dart库内容为:

代码语言:javascript
复制
// otherlib2库
library otherlib2;
class otherLib2 {}
void test2() {
  print('otherLib2库 : test2()函数');
}

(七)库的组成结构

库的最低要求是:pubspec.yaml文件和lib目录。 库的pubspec.yaml文件与普通应用程序包的文件格式相同。 lib目录:库代码位于lib 目录下,并且对其他包是公共的。您可以根据需要在lib下创建任何层次结构。 声明一个库的关键字是library。 例如在文件test.dart文件首行加上:library mylib; 表示这个库的名称是mylib


十一、异步支持

Dart库中包含许多返回Future或Stream对象的函数。这些函数是异步的:它们在设置可能耗时的操作(例如I / O)后返回,而不等待该操作完成。

Dart官网有关于异步的教学: 使用Future完成异步任务:https://www.dartlang.org/tutorials/language/futures 使用Streams(流)管理序列化数据:https://www.dartlang.org/tutorials/language/streams

asyncawait关键字支持异步编程,让你写异步代码看起来类似于同步代码。

(一)处理Future

当您需要完成Future的结果时,您有两个选择:

  • 1).使用async和await。
  • 2).使用Future API,如 库浏览 中所述。

(二)使用async和await

使用asyncawait异步的代码,但它看起来很像同步代码。例如,这里有一些代码await 用于等待异步函数的结果。例如:await lookUpVersion();

要使用async,代码必须在async函数中(标记为async的函数)。 例如:

代码语言:javascript
复制
Future checkVersion() async {
  var version = await lookUpVersion();
  // 其他操作
}

注意: 虽然async函数可能执行耗时的操作,但它不会等待这些操作。async函数只在遇到第一个await表达式时执行。然后它返回一个Future对象,仅在await表达式完成后才恢复执行。

使用try,catch,finally在使用await的代码中处理错误和清理代码。

代码语言:javascript
复制
try {
  version = await lookUpVersion();
} catch (e) {
  // 这里可以看到是什么错误。
}fianlly{
  // 正确的解决方式写在这里
}

您可以在异步功能中多次使用await。例如,以下代码等待三次函数结果:

代码语言:javascript
复制
var entrypoint = await findEntrypoint();
var exitCode = await runExecutable(entrypoint, args);
await flushThenExit(exitCode);

在await表达式中,表达式的值通常是Future; 如果不是,那么该值将自动包含在Future中。 这个Future对象表示返回一个对象的promise。 await表达式的值是返回的对象。 await表达式使执行暂停,直到该对象可用。

如果在使用await时遇到编译时错误,请确保await在async函数中。 例如,要在应用程序的main()函数中使用await,main()方法必须标记为async:以下是一个完整的示例代码:

代码语言:javascript
复制
`import 'dart:async';`
// 要在应用程序的main()函数中使用await,main()方法必须标记为async

Future main() async {
  checkVersion();
  print('In main: version is ${await lookUpVersion()}');
}

Future checkVersion() async {
  print('checkVersion()');
  var version = await lookUpVersion();
}

Future<String> lookUpVersion(){
  print('lookUpVersion()');
}
运行结果是:
  checkVersion()
lookUpVersion()
lookUpVersion()
In main: version is null

(三)定义一个异步函数

方法被async修饰的函数是异步函数。给一个函数添加async关键字,使得返回值是一个Future。

代码语言:javascript
复制
void main(){
  lookUpVersion(); //输出结果:lookUpVersion()同步方法 返回值是:1.0.0
  lookUpVersion2(); // 输出结果:lookUpVersion2()异步方法 返回值是:1.0.0
  lookUpVersion3(); // 输出结果:lookUpVersion3()异步方法 没有返回值
}

例如,看下面这个返回值是String的同步函数:

代码语言:javascript
复制
String lookUpVersion() {
      print('lookUpVersion()同步方法 返回值是:1.0.0');
      return '1.0.0';
}

如果将其更改为异步函数 - 例如,因为Future的实现将非常耗时 - 返回的值是Future:

代码语言:javascript
复制
Future<String> lookUpVersion2() async{
      print('lookUpVersion2()异步方法 返回值是:1.0.0');
      return '1.0.0';
}

如果您的函数没有返回有用的值,请设置其返回类型Future<void> 例如:

代码语言:javascript
复制
Future<void> lookUpVersion3() async {
  print('lookUpVersion3()异步方法 没有返回值');
}

(四)处理Stream

当您需要完成Future的结果时,您有两个选择:

  • 1).使用async和异步for循环(await for)。 注意:在使用await for之前,请确保它使代码更清晰,并且您确实希望等待所有Stream的结果。 例如,通常情况,不应该使用await for UI事件侦听器,因为UI框架会发送无穷无尽的事件流(streams of events)。
  • 2).使用Stream API(主要是IO操作。)

异步for循环的格式:await for(var或具体类型 标识符 in 表达式){} 例如:我们读取本地的一个文件内容,实例代码如下:

代码语言:javascript
复制
import 'dart:io';
import 'dart:convert';
void main() {
      test();
}
// await for循环的使用示例
// 这里是读取本地文件的内容
Future test() async {
      var config=File('d:\\test.txt');
      // 打开io流进行文件读取
      Stream<List<int>> inputStream = config.openRead();
      var lines = inputStream
        // 设置编码格式为utf-8
        .transform(utf8.decoder)
        .transform(LineSplitter());
      try {
          await for (var line in lines) {
              print('从Stream中获取到的内容是: ${line} \r文本内容长度为:'+ '${line.length}\r=======');
          }
        print('文件现在没有关闭。。。');
      } catch (e) {
        print(e);
      }
}

表达式的值必须有Stream类型,执行过程如下:

  • 1).等待,知道Stream发出一个数值。
  • 2).执行for循环的主体,讲变量设置为这个发出的数值。
  • 3).重复1和2,知道关闭Stream。

要停止监听Stream,你可以使用break或者return语句跳出for循环B并且从Stream中取消订阅。 如果在实现异步for循环时遇到编译时错误,确保await for在一个async函数中。 例如,要在应用程序的main()函数中使用await for循环,main()方法必须标记为async:以下是一个完整的示例代码:

代码语言:javascript
复制
Future main() async {
   // ...
  await for (var request in requestServer) {
    handleRequest(request);
  }
  // ...
}

有关异步编程的更多信息,请查看库浏览的 Dart库之旅 --- dart:async 部分,另请参阅文章 Dart语言异步支持:阶段2(该页面可能过期了) 和 Dart语言规范。


十二、Isolates

大多数计算机,甚至在移动平台上,都有多核CPU。为了利用所有这些核心,开发人员传统上使用并发运行的共享内存线程。但是,共享状态并发容易出错,并且可能导致代码复杂化。 所有Dart代码都在隔离区内运行,而不是线程。每个隔离区都有自己的内存堆,确保不会从任何其他隔离区访问隔离区的状态。

Dart是单线程模型,但是使用Isolates可以用于多线程。

这个库主要用于服务端的开发。如果你不使用Dart做服务端开发,仅作为了解即可。 源码可以看Github:https://github.com/dart-lang/isolate 官方API文档: https://api.dartlang.org/stable/2.1.0/dart-isolate/dart-isolate-library.html

使用isolate 需要先导入包:import 'dart:isolate';

下面来一个简单的示例代码: // 在另一个隔离区()中同步读取“D://file.json” // 结果是{msg: [{title: 你好1, contents: yes}, {title: 你好2, contents: NO}]} main() async { // 在其他隔离(isolate)中同步读取文件,然后对其进行解码。 print(await readIsolate()); }

// 同步读取'D//file.json'(在同一个线程中) Map readSync() { JsonCodec().decode(new File('D://file.json').readAsStringSync()); }

// 在另一个隔离区()中同步读取“D://file.json” Future readIsolate() async { final response = new ReceivePort(); await Isolate.spawn(_isolate, response.sendPort); return response.first; }

/// 期望通过[Isolate.spawn]创建 void _isolate(SendPort sendPort) { sendPort.send(readSync()); }

下面是file.json文件的内容: { "msg": [ { "title": "你好1", "contents": "yes" }, { "title": "你好2", "contents": "NO" } ] }


十三、生成器(Generators)

当您需要懒惰地生成一系列值时,请考虑使用生成器函数。Dart支持两种生成器功能。

(一)同步生成器,返回一个Iterable对象。

要实现同步生成器函数,请将函数体标记为sync*,并使用yield语句来传递值。

代码语言:javascript
复制
Iterable<int> naturalsTo(int n) sync* {
  int k = 0;
  while (k < n) yield k++;
}

如果您的生成器是递归的,您可以使用yield*以下方法来提高其性能:

代码语言:javascript
复制
Iterable<int> naturalsDownFrom(int n) sync* {
    if (n > 0) {
      yield n;
      yield* naturalsDownFrom(n - 1);
    }
}

(二)异步生成器,返回一个Stream对象。

要实现异步生成器函数,请将函数体标记为async*,并使用yield语句来传递值。

代码语言:javascript
复制
Stream<int> asynchronousNaturalsTo(int n) async* {
  int k = 0;
  while (k < n) yield k++;
}

十四、类型定义

在Dart中,函数是对象,就像字符串一样,数字是对象。一个类型定义,或功能型的别名,给出了一个函数类型声明字段时,您可以使用和返回类型的名称。当函数类型分配给变量时,typedef会保留类型信息。

以下代码,它不使用typedef:我们可以看到compare是一个函数,但它是哪一种类型的函数?不是很清楚。

代码语言:javascript
复制
class SortedCollection {
  Function compare;
  SortedCollection(int f(Object a, Object b)) {
    compare = f;
  }
}

// Initial, broken implementation.
int sort(Object a, Object b) => 0;

void main() {
  SortedCollection coll = SortedCollection(sort);

  // compare是一个函数,但它是哪一种类型的函数?
  assert(coll.compare is Function);
}

接下来使用typedef改造一下: 我们将代码更改为使用显式名称并保留类型信息,开发人员和工具都可以使用该信息。

代码语言:javascript
复制
typedef Compare = int Function(Object a, Object b);

class SortedCollection {
  Compare compare;
  SortedCollection(this.compare);
}

// Initial, broken implementation.
int sort(Object a, Object b) => 0;

void main() {
  SortedCollection coll = SortedCollection(sort);
  assert(coll.compare is Function);
  assert(coll.compare is Compare);
}

目前:typedef仅限于函数类型。

因为typedef只是别名,Dart提供了一种检查任何函数类型的方法。 例如:

代码语言:javascript
复制
typedef Compare<T> = int Function(T a, T b);
int sort(int a, int b) => a - b;
void main() {
  assert(sort is Compare<int>); // True
}

十五、元数据Metadata

使用元数据提供有关代码的其他信息。元数据注解以字符开头@,后跟对编译时常量(如deprecated)的引用或对常量构造函数的调用。 元数据可以出现在库,类,typedef,类型参数,构造函数,工厂,函数,字段,参数或变量声明之前以及导入或导出指令之前。您可以使用反射在运行时检索元数据。 所有Dart代码都有两个注解:@deprecated@override。 以下是使用@deprecated 注解的示例:

代码语言:javascript
复制
class Television {
  /// _Deprecated: Use [turnOn] instead._
  @deprecated
  void activate() {
    turnOn();
  }

  // Turns the TV's power on.
  void turnOn() {
//...
}
}

您可以定义自己的元数据注释。

这是一个定义带有两个参数的@todo注释的示例:

代码语言:javascript
复制
library todo;

class Todo {
  final String who;
  final String what;
  const Todo(this.who, this.what);
}

以下是使用@todo注释的示例:

代码语言:javascript
复制
import 'todo.dart';
@Todo('seth', 'make this do something')
void doSomething() {
  print('do something');
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018.12.30 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 九、泛型(Generics)
    • (一)为什么使用泛型?
      • (二)使用集合文字
        • (三)使用带有构造函数的参数化类型
          • (四)泛型集合及其包含的类型
            • (五)限制参数类型
              • (六)使用泛型方法
              • 十、库和可见性
                • (一)使用库
                  • (二)指定库前缀
                    • (三)仅导入库的一部分
                      • (四)懒加载一个库
                        • (五)库的拆分
                          • (六)库的自动导入
                            • (七)库的组成结构
                            • 十一、异步支持
                              • (一)处理Future
                                • (二)使用async和await
                                  • (三)定义一个异步函数
                                    • (四)处理Stream
                                    • 十二、Isolates
                                    • 十三、生成器(Generators)
                                      • (一)同步生成器,返回一个Iterable对象。
                                        • (二)异步生成器,返回一个Stream对象。
                                        • 十四、类型定义
                                        • 十五、元数据Metadata
                                        领券
                                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档