抛出错误后如何保持生成器函数产生值?

Bru*_*You 2 asynchronous dart

在 Dart 语言中,如何在保持生成值的同时在生成器函数中创建错误?

import 'dart:math' show Random;

main() {
  sum20RandomNumbers().then((sum) {
    print("Sum: $sum");
  });
}

Future<double> sum20RandomNumbers() async {
  var randomStream = generateRandomNumbers().handleError((error) {
    print('Ignore large number: ${error}');
    // How can I ignore error here?
  });

  int count = 0;
  double sum = 0;
  await for (var n in randomStream) {
    print(n);
    sum += n;
    if (++count >= 20) // sum 20 numbers at most
      break;
  }
  return sum;
}

Stream<double> generateRandomNumbers([int seed]) async* {
  final random = Random(seed);
  while (true) {
    final nextDouble = random.nextDouble();
    if (nextDouble > 0.8) {
      throw Exception('$nextDouble');
      // how can I keep generating next random numbers?
    }
    yield nextDouble;
  }
}
Run Code Online (Sandbox Code Playgroud)

上面的代码显示了sum20RandomNumbers()将 20 个随机数加起来的意图,但是在生成器函数中抛出异常后它将停止generateRandomNumbers()。抛出错误后如何保持生成器函数产生值?

lrn*_*lrn 5

对于sync*函数,您不能这样做。

Iterable报告错误的唯一方法是使IteratormoveNext方法抛出。为此,sync*方法必须抛出。当sync*函数抛出时,它会终止函数体,这使得该函数体无法继续并产生更多值。

对于async*抛出的函数也会发生同样的事情,但是你也有一种不同的方式来发出错误。因为流可以作为流的一部分报告错误,所以可以发出错误并继续,但不能通过抛出.

Stream<double> generateRandomNumbers([int seed]) async* {
  final random = Random(seed);
  while (true) {
    final nextDouble = random.nextDouble();
    if (nextDouble > 0.8) {
      yield* Future<double>.error(Exception('$nextDouble')).asStream();
      continue;
    }
    yield nextDouble;
  }
}    
Run Code Online (Sandbox Code Playgroud)

诀窍是yield*包含错误的流。你可以用很多不同的方式做到这一点。

yield* Stream.error(theError);
Run Code Online (Sandbox Code Playgroud)

这个构造函数是在 Dart 2.5 中添加的,是最简单的方法。其他替代方案是:

yield* Future<double>.error(theError).asStream();
Run Code Online (Sandbox Code Playgroud)

或者

yield* () async* { throw theError; } ();
Run Code Online (Sandbox Code Playgroud)

或者

yield* (StreamController<double>()..addError(theError)..close()).stream;
Run Code Online (Sandbox Code Playgroud)