openNativeAsync<T> static method
Opens a single-use WebReceivePort, hands its port id to call so the
WASM implementation can post the result through the module post
callback, then waits for exactly one message and converts it with
unpack. Mirrors the native contract, including
NitroConfig.nativeAsyncTimeoutMs.
Implementation
static Future<T> openNativeAsync<T>({
required void Function(int dartPort) call,
required T Function(dynamic raw) unpack,
void Function()? cleanup,
String methodName = '',
}) {
final cfg = NitroConfig.instance;
final effective = cfg.effectiveLogLevel;
final traceTimeline = cfg.timelineTracingEnabled;
final timeoutMs = cfg.nativeAsyncTimeoutMs;
final sw = effective == NitroLogLevel.verbose || cfg.slowCallThresholdUs > 0 ? (Stopwatch()..start()) : null;
String tag() => methodName.isEmpty ? 'nativeAsync' : 'nativeAsync($methodName)';
if (effective == NitroLogLevel.verbose) _log(NitroLogLevel.verbose, tag(), 'calling');
final port = WebReceivePort();
if (traceTimeline) developer.Timeline.startSync(_timelineLabel(tag()));
void terminate() {
port.close();
cleanup?.call();
if (traceTimeline) developer.Timeline.finishSync();
}
try {
call(port.sendPort.nativePort);
} catch (e, st) {
terminate();
if (effective != NitroLogLevel.none) {
_log(NitroLogLevel.error, tag(), 'threw: $e', e, st);
}
rethrow;
}
T handle(dynamic raw) {
if (sw != null) _logCallTiming(sw, tag());
try {
return unpack(raw);
} catch (e, st) {
if (effective != NitroLogLevel.none) {
_log(NitroLogLevel.error, tag(), 'threw during unpack: $e', e, st);
}
rethrow;
}
}
if (timeoutMs <= 0) {
return port.first.then(handle).whenComplete(terminate);
}
final completer = Completer<dynamic>();
final timer = Timer(Duration(milliseconds: timeoutMs), () {
if (!completer.isCompleted) {
completer.completeError(
TimeoutException('${tag()} did not post a result within ${timeoutMs}ms', Duration(milliseconds: timeoutMs)),
);
}
});
final sub = port.listen((msg) {
if (!completer.isCompleted) completer.complete(msg);
});
return completer.future.then(handle).whenComplete(() {
timer.cancel();
sub.cancel();
terminate();
});
}