当前位置: 首页 > news >正文

Dart之库和可见性和异步支持、生成器、可调用类与Isolates、Typedefs和元数据

import 和 library 指令可以用来创建一个模块化的,可共享的代码库。 库不仅提供了 API ,而且对代码起到了封装的作用: 以下划线 (_) 开头的标识符仅在库内可见。 每个 Dart 应用程序都是一个库 ,虽然没有使用 library 指令。

库可以通过包来分发。有关 pub(集成在SDK中的包管理器)的信息,请参考 Pub Package 和 Asset Manager。

使用库

通过 import 指定一个库命名空间中的内如如何在另一个库中使用。 例如,Dart Web应用程序通常使用 dart:html库,它们可以像这样导入:

import 'dart:html';

import 参数只需要一个指向库的 URI。 对于内置库,URI 拥有自己特殊的dart: 方案。 对于其他的库,使用系统文件路径或者 package: 方案 。 package: 方案指定由包管理器(如 pub 工具)提供的库。例如:

import 'package:test/test.dart';

提示: URI 代表统一资源标识符。 URL(统一资源定位符)是一种常见的URI。

指定库前缀

如果导入两个存在冲突标识符的库, 则可以为这两个库,或者其中一个指定前缀。 例如,如果 library1 和 library2 都有一个 Element 类, 那么可以通过下面的方式处理:

import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;

// 使用 lib1 中的 Element。
Element element1 = Element();

// 使用 lib2 中的 Element。
lib2.Element element2 = lib2.Element();
导入库的一部分

如果你只使用库的一部分功能,则可以选择需要导入的 内容。例如:

// Import only foo.
import 'package:lib1/lib1.dart' show foo;

// Import all names EXCEPT foo.
import 'package:lib2/lib2.dart' hide foo;
延迟加载库

Deferred loading (也称之为 lazy loading) 可以让应用在需要的时候再加载库。 下面是一些使用延迟加载库的场景:

  • 减少 APP 的启动时间。
  • 执行 A/B 测试,例如 尝试各种算法的 不同实现。
  • 加载很少使用的功能,例如可选的屏幕和对话框。

要延迟加载一个库,需要先使用 deferred as 来导入:

import 'package:greetings/hello.dart' deferred as hello;

当需要使用的时候,使用库标识符调用 loadLibrary() 函数来加载库:

Future greet() async {
  await hello.loadLibrary();
  hello.printGreeting();
}

在前面的代码,使用 await 关键字暂停代码执行一直到库加载完成。 关于 async 和 await 的更多信息请参考 异步支持。

在一个库上你可以多次调用 loadLibrary() 函数。但是该库只是载入一次。

使用延迟加载库的时候,请注意一下问题:

  • 延迟加载库的常量在导入的时候是不可用的。 只有当库加载完毕的时候,库中常量才可以使用。
  • 在导入文件的时候无法使用延迟库中的类型。 如果你需要使用类型,则考虑把接口类型移动到另外一个库中, 让两个库都分别导入这个接口库。
  • Dart 隐含的把 loadLibrary() 函数导入到使用 deferred as 的命名空间 中。 loadLibrary() 方法返回一个 Future。

Dart VM difference: The Dart VM allows access to members of deferred libraries even before the call to loadLibrary(). This behavior might change, so don’t depend on the current VM behavior. For details, see issue #33118.

实现库

有关如何实现库包的建议,请参考 Create Library Packages 这里面包括:

  • 如何组织库的源文件。
  • 如何使用 export 命令。
  • 何时使用 part 命令。
  • 何时使用 library 命令。

异步支持

Dart 库中包含许多返回 Future 或 Stream 对象的函数. 这些函数在设置完耗时任务(例如 I/O 曹组)后, 就立即返回了,不会等待耗任务完成。 使用 async 和 await 关键字实现异步编程。 可以让你像编写同步代码一样实现异步操作。

处理 Future

可以通过下面两种方式,获得 Future 执行完成的结果:

  • 使用 async 和 await.
  • 使用 Future API,具体描述,参考 库概览.

使用 async 和 await 关键字的代码是异步的。 虽然看起来有点像同步代码。 例如,下面的代码使用 await 等待异步函数的执行结果。

await lookUpVersion();

要使用 await , 代码必须在 异步函数(使用 async 标记的函数)中:

Future checkVersion() async {
  var version = await lookUpVersion();
  // Do something with version
}

提示: 虽然异步函数可能会执行耗时的操作, 但它不会等待这些操作。 相反,异步函数只有在遇到第一个 await 表达式(详情见)时才会执行。 也就是说,它返回一个 Future 对象, 仅在await表达式完成后才恢复执行。

使用 try, catch, 和 finally 来处理代码中使用 await 导致的错误。

try {
  version = await lookUpVersion();
} catch (e) {
  // React to inability to look up the version
}

在一个异步函数中可以多次使用 await 。 例如,下面代码中等待了三次函数结果:

var entrypoint = await findEntrypoint();
var exitCode = await runExecutable(entrypoint, args);
await flushThenExit(exitCode);

在 await 表达式 中, 表达式 的值通常是一个 Future 对象; 如果不是,这是表达式的值会被自动包装成一个 Future 对象。 Future 对象指明返回一个对象的承诺(promise)。 await 表达式 执行的结果为这个返回的对象。 await 表达式会阻塞代码的执行,直到需要的对象返回为止。

如果在使用 await 导致编译时错误, 确认 await 是否在一个异步函数中。 例如,在应用的 main() 函数中使用 await , main() 函数的函数体必须被标记为 async :

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

声明异步函数

函数体被 async 标示符标记的函数,即是一个_异步函数_。 将 async 关键字添加到函数使其返回Future。 例如,考虑下面的同步函数,它返回一个 String :

String lookUpVersion() => '1.0.0';

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

Future<String> lookUpVersion() async => '1.0.0';

注意,函数体不需要使用Future API。 如有必要, Dart 会创建 Future 对象。

如果函数没有返回有效值, 需要设置其返回类型为 Future<void> 。

处理 Stream

当需要从 Stream 中获取数据值时, 可以通过一下两种方式:

  • 使用 async 和 一个 异步循环 (await for)。
  • 使用 Stream API, 更多详情,参考 in the library tour。

提示: 在使用 await for 前,确保代码清晰, 并且确实希望等待所有流的结果。 例如,通常不应该使用 await for 的UI事件侦听器, 因为UI框架会发送无穷无尽的事件流。

以下是异步for循环的使用形式:

await for (varOrType identifier in expression) {
  // Executes each time the stream emits a value.
}

上面 表达式 返回的值必须是 Stream 类型。 执行流程如下:

  1. 等待,直到流发出一个值。
  2. 执行 for 循环体,将变量设置为该发出的值
  3. 重复1和2,直到关闭流。

使用 break 或者 return 语句可以停止接收 stream 的数据, 这样就跳出了 for 循环, 并且从 stream 上取消注册。 **如果在实现异步 for 循环时遇到编译时错误, 请检查确保 await for 处于异步函数中。** 例如,要在应用程序的 main() 函数中使用异步 fo r循环, main() 函数体必须标记为 async` :

Future main() async {
  // ...
  await for (var request in requestServer) {
    handleRequest(request);
  }
  // ...
}

有关异步编程的更多信息,请参考 dart:async 部分。 同时也可参考文章 Dart Language Asynchrony Support: Phase 1 和 Dart Language Asynchrony Support: Phase 2, 以及 Dart language specification 。

生成器
 

当您需要延迟生成( lazily produce )一系列值时, 可以考虑使用_生成器函数_。 Dart 内置支持两种生成器函数:

  • Synchronous 生成器: 返回一个 Iterable 对象。
  • Asynchronous 生成器: 返回一个 Stream 对象。

通过在函数体标记 sync*, 可以实现一个同步生成器函数。 使用 yield 语句来传递值

Iterable<int> naturalsTo(int n) sync* {
  int k = 0;
  while (k < n) yield k++;
}

通过在函数体标记 async*, 可以实现一个异步生成器函数。 使用 yield 语句来传递值:

Stream<int> asynchronousNaturalsTo(int n) async* {
  int k = 0;
  while (k < n) yield k++;
}

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

Iterable<int> naturalsDownFrom(int n) sync* {
  if (n > 0) {
    yield n;
    yield* naturalsDownFrom(n - 1);
  }
}

有关生成器的更多信息,请参考文章 Dart Language Asynchrony Support: Phase 2 .

可调用类与Isolates

通过实现类的 call() 方法, 能够让类像函数一样被调用。

在下面的示例中,WannabeFunction 类定义了一个 call() 函数, 函数接受三个字符串参数,函数体将三个字符串拼接,字符串间用空格分割,并在结尾附加了一个感叹号。

class WannabeFunction {
  call(String a, String b, String c) => '$a $b $c!';
}

main() {
  var wf = new WannabeFunction();
  var out = wf("Hi","there,","gang");
  print('$out');
}

有关把类当做方法使用的更多信息,请参考 Emulating Functions in Dart 。

Isolates

大多数计算机中,甚至在移动平台上,都在使用多核CPU。 为了有效利用多核性能,开发者一般使用共享内存数据来保证多线程的正确执行。 然而, 多线程共享数据通常会导致很多潜在的问题,并导致代码运行出错。

所有 Dart 代码都在隔离区( isolates )内运行,而不是线程。 每个隔离区都有自己的内存堆,确保每个隔离区的状态都不会被其他隔离区访问。

Typedefs
 

在 Dart 中,函数也是对象,就像字符和数字对象一样。 使用 typedef ,或者 function-type alias 为函数起一个别名, 别名可以用来声明字段及返回值类型。 当函数类型分配给变量时,typedef会记录类型信息。

请考虑以下代码,代码中未使用 typedef :

class SortedCollection {
  Function compare;

  SortedCollection(int f(Object a, Object b)) {
    compare = f;
  }
}

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

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

  // 虽然知道 compare 是函数,
  // 但是函数是什么类型 ?
  assert(coll.compare is Function);
}

当把 f 赋值给 compare 的时候,类型信息丢失了。 f 的类型是 (Object, Object) → int (这里 → 代表返回值类型), 但是 compare 得到的类型是 Function 。如果我们使用显式的名字并保留类型信息, 这样开发者和工具都可以使用这些信息:

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;

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

提示: 目前,typedefs 只能使用在函数类型上, 我们希望将来这种情况有所改变。

由于 typedefs 只是别名, 他们还提供了一种方式来判断任意函数的类型。例如:

 
typedef int Compare(int a, int b); int sort(int a, int b) => a - b; main() { assert(sort is Compare); // True! }

元数据

使用元数据可以提供有关代码的其他信息。 元数据注释以字符 @ 开头, 后跟对编译时常量 (如 deprecated) 的引用或对常量构造函数的调用。

对于所有 Dart 代码有两种可用注解:@deprecated 和 @override。 关于 @override 的使用, 参考 扩展类(继承)。 下面是使用 @deprecated 注解的示例:

class Television {
  /// _Deprecated: Use [turnOn] instead._
  @deprecated
  void activate() {
    turnOn();
  }

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

可以自定义元数据注解。 下面的示例定义了一个带有两个参数的 @todo 注解:

library todo;

class Todo {
  final String who;
  final String what;

  const Todo(this.who, this.what);
}

使用 @todo 注解的示例:

import 'todo.dart';

@Todo('seth', 'make this do something')
void doSomething() {
  print('do something');
}

元数据可以在 library、 class、 typedef、 type parameter、 constructor、 factory、 function、 field、 parameter 或者 variable 声明之前使用,也可以在 import 或者 export 指令之前使用。 使用反射可以在运行时获取元数据信息。

注释

单行注释

单行注释以 // 开始。 所有在 // 和改行结尾之间的内容被编译器忽略。

void main() {
  // TODO: refactor into an AbstractLlamaGreetingFactory?
  print('Welcome to my Llama farm!');
}

多行注释

多行注释以 /* 开始, 以 */ 结尾。 所有在 /* 和 */ 之间的内容被编译器忽略 (不会忽略文档注释)。 多行注释可以嵌套。

void main() {
  /*
   * This is a lot of work. Consider raising chickens.

  Llama larry = Llama();
  larry.feed();
  larry.exercise();
  larry.clean();
   */
}

文档注释

文档注释可以是多行注释,也可以是单行注释, 文档注释以 /// 或者 /** 开始。 在连续行上使用 /// 与多行文档注释具有相同的效果。

在文档注释中,除非用中括号括起来,否则Dart 编译器会忽略所有文本。 使用中括号可以引用类、 方法、 字段、 顶级变量、 函数、 和参数。 括号中的符号会在已记录的程序元素的词法域中进行解析。

下面是一个引用其他类和成员的文档注释:

/// A domesticated South American camelid (Lama glama).
///
/// 自从西班牙时代以来,
/// 安第斯文化就将骆驼当做肉食类和运输类动物。
class Llama {
  String name;

  /// 喂养骆驼 [Food].
  ///
  /// 典型的美洲驼每周吃一捆干草。
  void feed(Food food) {
    // ...
  }

  /// 使用 [activity] 训练骆驼
  /// [timeLimit] 分钟。
  void exercise(Activity activity, int timeLimit) {
    // ...
  }
}

在生成的文档中,[Food] 会成为一个链接, 指向 Food 类的 API 文档。

相关文章:

  • 微前端 - 以无界为例
  • C语言库zlog日志库工具
  • 23种设计模式-结构型模式-组合
  • RabbitMQ消息队列面试题集合
  • 如何使用 FastAPI 构建 MCP 服务器
  • 【电动汽车再生制动控制技术(万字长文,图文并茂)】
  • 全国职业院校技能大赛 网络建设与运维样题解析
  • 11-SpringBoot3入门-整合aop
  • 分布式计算Ray框架面试题及参考答案
  • 喜讯 | 耘瞳科技视觉检测与测量装备荣膺“2024机器视觉创新产品TOP10”
  • SerDes(Serializer/Deserializer)详解
  • SOME/IP-SD -- 协议英文原文讲解10
  • 广度优先搜索(BFS)与深度优先搜索(DFS)解析
  • 通义万相2.1 你的视频创作之路
  • Web-ssrfme:redis 未授权访问攻击
  • 【go】数组与切片
  • GoLand 2024.3 中文 GO语言开发工具
  • 什么是架构,以及当前市面主流架构类型有哪些?
  • 智能车载终端测试:慧通测控多参数综合测试定制化方案
  • <em>彩</em><em>票</em><em>导</em><em>师</em><em>带</em><em>玩</em><em>群</em>
  • 土地流转网站建设报告/百度平台电话
  • word模板免费下载网站/南宁网站公司
  • 来个网站吧好人一生平安2022/app软件下载站seo教程
  • 湖北政府门户网站建设研究/最新的新闻 最新消息
  • 手机网站模板 织梦/友情链接交换标准
  • word如何做网站/企业网络推广技巧