run method
Executes the computation and returns the computation result (or throws the exception), or throws the CancellationException exception if the terminate method was called before the computation was completed.
Implementation
@override
Future<T> run() async {
if (_isStarted) {
throw StateError('The computation can be run only once');
}
_isStarted = true;
if (_isTerminationRequested) {
throw CancellationException();
}
if (_token != null) {
_token!.throwIfCanceled();
}
final completer = Completer<void>();
var hasResult = false;
var isCanceled = false;
T? result;
Object? error;
StackTrace? stackTrace;
final port = RawReceivePort();
final sendPort = port.sendPort;
SendPort? remotePort;
void sendCancellationRequest() {
remotePort?.send(null);
}
void handle(Object? message) {
if (message is SendPort) {
remotePort = message;
return;
}
port.close();
if (message == null) {
isCanceled = _isTerminationRequested;
} else if (message is List) {
error = message[0];
final stackTraceString = message[1] as String?;
if (stackTraceString != null && stackTraceString.isNotEmpty) {
stackTrace = StackTrace.fromString(stackTraceString);
}
} else if (message is (String, Object?)) {
final (event, value) = message;
switch (event) {
case 'result':
hasResult = true;
result = value as T;
break;
case 'cancellation':
isCanceled = true;
break;
}
}
_token?.removerHandler(sendCancellationRequest);
completer.complete();
}
port.handler = handle;
_token?.addHandler(sendCancellationRequest);
final isolate =
await Isolate.spawn<(SendPort, FutureOr<T> Function() function)>(
_compute,
(sendPort, _computation),
onError: sendPort,
onExit: sendPort,
paused: true,
);
_isolate = isolate;
isolate.resume(isolate.pauseCapability!);
await completer.future;
if (error != null) {
Error.throwWithStackTrace(error!, stackTrace ?? StackTrace.empty);
} else if (hasResult) {
return result as T;
} else if (isCanceled) {
throw CancellationException();
} else {
throw StateError('Computation ended without result');
}
}