Dart语法基础补充

Asynchrony support

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

async 和 await 关键字支持异步编程,让编写看起来类似于同步代码的异步代码。

参考:Future class - dart:async library - Dart API

Stream class - dart:async library - Dart API

Handling Futures

需要已完成的 Future 的结果时,有两种选择:

  • 使用异步和等待。
  • 使用 Future API。

使用 async 和 await 的代码是异步的,但它看起来很像同步代码。 例如,下面是一些使用 await 等待异步函数结果的代码:

await lookUpVersion();

 要使用 await,代码必须在异步函数中——标记为异步的函数:

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

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

在使用 await 的代码中使用 try、catch 和 finally 来处理错误和清理:

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 对象指示返回对象的承诺。 await 表达式的值是返回的对象。 await 表达式使执行暂停,直到该对象可用。

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

 注意:前面的示例使用异步函数 (checkVersion()) 而不等待结果——如果代码假定函数已完成执行,这种做法可能会导致问题。 

Declaring async functions

 异步函数是函数体标有 async 修饰符的函数。

将 async 关键字添加到函数使其返回 Future。 例如,考虑这个同步函数,它返回一个字符串:

String lookUpVersion() => '1.0.0';

 把它改成一个异步函数——例如,因为Future的实现会很耗时——返回的值是一个Future:

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

请注意,函数的主体不需要使用 Future API。 如果需要,Dart 会创建 Future 对象。 如果函数没有返回有用的值,请将其返回类型设置为 Future<void>。

Handling Streams

需要从Stream中获取值时,有两种选择:

  • 使用异步和异步 for 循环(等待)。
  • 使用 Stream API。

注意:在使用 await for 之前,请确保它使代码更清晰,并且确实想要等待Stream的所有结果。 例如,通常不应该为 UI 事件侦听器使用 await for,因为 UI 框架会发送无穷无尽的事件流。

异步 for 循环具有以下形式:

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

 expression 的值必须具有 Stream 类型。 执行过程如下:

  • 等到Stream发出一个值。
  • 执行 for 循环的主体,并将变量设置为发出的值。
  • 重复 1 和 2,直到Stream关闭。

要停止收听Stream,可以使用 break 或 return 语句,它会跳出 for 循环并取消订阅流。

如果在实现异步 for 循环时出现编译时错误,请确保 await for 位于异步函数中。 例如,要在应用程序的 main() 函数中使用异步 for 循环,必须将 main() 的主体标记为异步:

void main() async {
  // ...
  await for (final request in requestServer) {
    handleRequest(request);
  }
  // ...
}

 异步编程参考:

Library tour | Dart

Generators

需要延迟生成一系列值时,请考虑使用生成器函数。 Dart 内置支持两种生成器函数:

实现一个同步生成器函数,将函数体标记为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);
  }
}

Callable classes

要允许 Dart 类的实例像函数一样被调用,请实现 call() 方法。

call() 方法允许任何定义它的类来模拟一个函数。 此方法支持与普通函数相同的功能,例如参数和返回类型。

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

var wf = WannabeFunction();
var out = wf('Hi', 'there,', 'gang');

void main() => print(out);

Log
Hi there, gang!

Isolates

大多数计算机,甚至在移动平台上,都具有多核 CPU。 为了利用所有这些内核,开发人员传统上使用并发运行的共享内存线程。 然而,共享状态并发很容易出错,并可能导致代码复杂化。

所有 Dart 代码都运行在 isolates 中,而不是线程。 每个 Dart isolate 使用单个执行线程并且不与其他 isolate 共享可变对象。 旋转多个隔离创建多个执行线程。 这使得多线程没有其主要缺点,竞争条件。https://en.wikipedia.org/wiki/Race_condition#In_software

Typedefs

类型别名——通常称为 typedef,因为它是用关键字 typedef 声明的——是一种引用类型的简洁方式。 下面是声明和使用名为 IntList 的类型别名的示例:

typedef IntList = List<int>;
IntList il = [1, 2, 3];

类型别名可以有类型参数:

typedef ListMapper<X> = Map<X, List<X>>;
Map<String, List<String>> m1 = {}; // Verbose.
ListMapper<String> m2 = {}; // Same thing but shorter and clearer.

 在大多数情况下,建议对函数使用内联函数类型而不是 typedef。 但是,函数 typedef 仍然有用:

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!
}

猜你喜欢

转载自blog.csdn.net/zhangying1994/article/details/128995624