runAsync<P, R> method
Spawns an isolate, runs callback
in that isolate passing it param
with
its own Store and returns the result of callback.
This is useful for ObjectBox operations that take longer than a few milliseconds, e.g. putting many objects, which would cause frame drops. If all operations can execute within a single transaction, prefer to use runInTransactionAsync.
The Store given to the callback does not have to be closed, it is closed by the worker isolate once the callback returns (or throws).
The following example gets the name of a User object, deletes the object and returns the name:
String? readNameAndRemove(Store store, int objectId) {
var box = store.box<User>();
final nameOrNull = box.get(objectId)?.name;
box.remove(objectId);
return nameOrNull;
}
await store.runAsync(readNameAndRemove, objectId);
The callback
must be a function that can be sent to an isolate: either a
top-level function, static method or a closure that only captures objects
that can be sent to an isolate.
Warning: Due to dart-lang/sdk#36983 a closure may capture more objects than expected, even if they are not directly used in the closure itself.
The types P
(type of the parameter to be passed to the callback) and
R
(type of the result returned by the callback) must be able to be sent
to or received from an isolate. The same applies to errors originating
from the callback.
See SendPort.send for a discussion on which values can be sent to and received from isolates.
Implementation
Future<R> runAsync<P, R>(RunAsyncCallback<P, R> callback, P param) async {
final port = RawReceivePort();
final completer = Completer<dynamic>();
void cleanup() {
port.close();
}
port.handler = (dynamic message) {
cleanup();
completer.complete(message);
};
try {
// Await isolate spawn to avoid waiting forever if it fails to spawn.
await Isolate.spawn(
_callFunctionWithStoreInIsolate<P, R>,
_RunAsyncIsolateConfig(
configuration(), port.sendPort, callback, param),
errorsAreFatal: true,
onError: port.sendPort,
onExit: port.sendPort);
} on Object {
cleanup();
rethrow;
}
final dynamic response = await completer.future;
if (response == null) {
throw RemoteError('Isolate exited without result or error.', '');
}
if (response is _RunAsyncResult) {
// Success, return result.
return response.result as R;
} else if (response is List<dynamic>) {
// See isolate.addErrorListener docs for message structure.
assert(response.length == 2);
await Future<Never>.error(RemoteError(
response[0] as String,
response[1] as String,
));
} else {
// Error thrown by callback.
assert(response is _RunAsyncError);
response as _RunAsyncError;
await Future<Never>.error(
response.error,
response.stack,
);
}
}