Skip to content

Instantly share code, notes, and snippets.

@alifgiant
Created July 27, 2022 02:18
Show Gist options
  • Save alifgiant/20f85f53d810a41298f0c372ee2843c3 to your computer and use it in GitHub Desktop.
Save alifgiant/20f85f53d810a41298f0c372ee2843c3 to your computer and use it in GitHub Desktop.
Reuse Dart isolate for json parsing
import 'dart:async';
import 'dart:convert';
import 'dart:isolate';
void _runner(SendPort toMainPort) {
final ReceivePort fromMainPort = ReceivePort();
toMainPort.send(fromMainPort.sendPort);
fromMainPort.listen((final message) {
if (message == 'kill') fromMainPort.close();
if (message is! Map<String, dynamic>) return;
try {
// heavy jsonDecode process
final result = jsonDecode(message['payload']);
toMainPort.send({'key': message['key'], 'result': result});
} catch (e) {
toMainPort.send({'key': message['key'], 'error': e.toString()});
}
});
}
class IsolatedJsonParser {
final fromIsolatePort = ReceivePort();
final isolateSetupCompleter = Completer();
Map<String, Completer<Map<String, dynamic>>> completerMap = {};
late Isolate isolate;
late SendPort _toIsolatePort;
IsolatedJsonParser._() {
Future.microtask(() async {
// only setup isolate once
// TODO(alifakbar): use multiple isolate (but has maximum count)
isolate = await Isolate.spawn(_runner, fromIsolatePort.sendPort);
fromIsolatePort.listen((message) {
if (message is SendPort) {
_toIsolatePort = message;
isolateSetupCompleter.complete(message);
}
if (message is! Map<String, dynamic>) return;
final key = message['key'];
final completer = completerMap[key];
if (completer == null) return;
if (message['result'] != null) {
completer.complete(message['result']);
} else if (message['error'] != null) {
completer.completeError(message['error']);
} else {
completer.completeError(Error());
}
completerMap.remove(key);
});
});
}
Future<P> parse<P>(
String path,
String payload,
P Function(Map<String, dynamic>) fromJson,
) async {
// make sure isolate is ready
if (!isolateSetupCompleter.isCompleted) {
await isolateSetupCompleter.future;
}
String key = path;
if (completerMap.containsKey(key)) key += 'N';
final completer = Completer<Map<String, dynamic>>();
completerMap[key] = completer;
_toIsolatePort.send({
'key': key,
'payload': payload,
});
final jsonMap = await completer.future;
return fromJson(jsonMap);
}
void close() {
Future.microtask(() async {
if (!isolateSetupCompleter.isCompleted) {
await isolateSetupCompleter.future;
}
_toIsolatePort.send('kill');
fromIsolatePort.close();
isolate.kill();
});
}
static IsolatedJsonParser? _i;
static IsolatedJsonParser get() {
_i ??= IsolatedJsonParser._();
return _i!;
}
static void dispose() {
_i?.close();
_i = null;
}
}
class Person {
final String name;
const Person(this.name);
factory Person.fromJson(Map<String, dynamic> json) {
return Person(json['name']);
}
}
void main(List<String> args) async {
final payload = '{"name":"alif"}';
final person = await IsolatedJsonParser.get().parse(
'/get-profile', // used as key for completer
payload,
(json) => Person.fromJson(json),
);
print(person.name);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment