run<R> 静态方法

  1. @Since("2.19")
Future<R> run<R>(
  1. FutureOr<R> computation(), {
  2. String? debugName,
})

在新的隔离器中运行 computation 并返回结果。

int slowFib(int n) =>
    n <= 1 ? 1 : slowFib(n - 1) + slowFib(n - 2);

// Compute without blocking current isolate.
var fib40 = await Isolate.run(() => slowFib(40));

如果 computation 是异步的(返回一个 Future<R>),则在该隔离器中等待该 future,完成整个异步计算,然后再返回结果。

int slowFib(int n) =>
    n <= 1 ? 1 : slowFib(n - 1) + slowFib(n - 2);
Stream<int> fibStream() async* {
  for (var i = 0;; i++) yield slowFib(i);
}

// Returns `Future<int>`.
var fib40 = await Isolate.run(() => fibStream().elementAt(40));

如果 computation 抛出异常,则隔离器将终止,并且此函数将抛出相同的错误。

Future<int> eventualError() async {
  await Future.delayed(const Duration(seconds: 1));
  throw StateError("In a bad state!");
}

try {
  await Isolate.run(eventualError);
} on StateError catch (e, s) {
  print(e.message); // In a bad state!
  print(LineSplitter.split("$s").first); // Contains "eventualError"
}

任何未被捕获的异步错误也将终止计算,但将被报告为 RemoteError,因为 addErrorListener 不提供原始错误对象。

结果是通过 exit 发送的,这意味着它被发送到这个隔离器而没有复制。

computation 函数及其结果(或错误)必须在隔离器之间发送。无法发送的对象包括打开的文件和套接字(有关详细信息,请参阅 SendPort.send)。

如果 computation 是一个闭包,则由于 Dart 实现的限制,它可能将意外的状态隐式发送到隔离器。这可能会导致性能问题、内存使用增加(请参阅 http://dartbug.com/36983)或,如果状态包含无法在隔离器之间传递的对象,则可能导致运行时失败。


void serializeAndWrite(File f, Object o) async {
  final openFile = await f.open(mode: FileMode.append);
  Future writeNew() async {
    // Will fail with:
    // "Invalid argument(s): Illegal argument in isolate message"
    // because `openFile` is captured.
    final encoded = await Isolate.run(() => jsonEncode(o));
    await openFile.writeString(encoded);
    await openFile.flush();
    await openFile.close();
  }

  if (await openFile.position() == 0) {
    await writeNew();
  }
}

在这种情况下,您可以为 Isolate.run 创建一个新的函数来调用,该函数将所有必需的状态作为参数。


void serializeAndWrite(File f, Object o) async {
  final openFile = await f.open(mode: FileMode.append);
  Future writeNew() async {
    Future<String> encode(o) => Isolate.run(() => jsonEncode(o));
    final encoded = await encode(o);
    await openFile.writeString(encoded);
    await openFile.flush();
    await openFile.close();
  }

  if (await openFile.position() == 0) {
    await writeNew();
  }
}

debugName 仅用于为调试命名新的隔离器。

实现

@Since("2.19")
static Future<R> run<R>(FutureOr<R> computation(), {String? debugName}) {
  var result = Completer<R>();
  var resultPort = RawReceivePort();
  resultPort.handler = (response) {
    resultPort.close();
    if (response == null) {
      // onExit handler message, isolate terminated without sending result.
      result.completeError(
          RemoteError("Computation ended without result", ""),
          StackTrace.empty);
      return;
    }
    var list = response as List<Object?>;
    if (list.length == 2) {
      var remoteError = list[0];
      var remoteStack = list[1];
      if (remoteStack is StackTrace) {
        // Typed error.
        result.completeError(remoteError!, remoteStack);
      } else {
        // onError handler message, uncaught async error.
        // Both values are strings, so calling `toString` is efficient.
        var error =
            RemoteError(remoteError.toString(), remoteStack.toString());
        result.completeError(error, error.stackTrace);
      }
    } else {
      assert(list.length == 1);
      result.complete(list[0] as R);
    }
  };
  try {
    Isolate.spawn(_RemoteRunner._remoteExecute,
            _RemoteRunner<R>(computation, resultPort.sendPort),
            onError: resultPort.sendPort,
            onExit: resultPort.sendPort,
            errorsAreFatal: true,
            debugName: debugName)
        .then<void>((_) {}, onError: (error, stack) {
      // Sending the computation failed asynchronously.
      // Do not expect a response, report the error asynchronously.
      resultPort.close();
      result.completeError(error, stack);
    });
  } on Object {
    // Sending the computation failed synchronously.
    // This is not expected to happen, but if it does,
    // the synchronous error is respected and rethrown synchronously.
    resultPort.close();
    rethrow;
  }
  return result.future;
}