description

# run<R> static method

1. @Since.new("2.19")

Future<R> run<R>(

1. FutureOr<R> computation(), {
2. String? debugName,

})

Runs `computation` in a new isolate and returns the result.

    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));

If `computation` is asynchronous (returns a `Future<R>`) then that future is awaited in the new isolate, completing the entire asynchronous computation, before returning the result.

    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));

If `computation` throws, the isolate is terminated and this function throws the same error.

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

Any uncaught asynchronous errors will terminate the computation as well, but will be reported as a RemoteError because addErrorListener does not provide the original error object.

The result is sent using exit, which means it's sent to this isolate without copying.

The `computation` function and its result (or error) must be sendable between isolates. Objects that cannot be sent include open files and sockets (see SendPort.send for details).

If `computation` is a closure then it may implicitly send unexpected state to the isolate due to limitations in the Dart implementation. This can cause performance issues, increased memory usage (see http://dartbug.com/36983) or, if the state includes objects that can't be spent between isolates, a runtime failure.

    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();
      }
    }

In such cases, you can create a new function to call Isolate.run that takes all of the required state as arguments.

    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();
      }
    }

The `debugName` is only used to name the new isolate for debugging.

## Implementation

    @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;
    }