d-markey / squadron

Multithreading and worker thread pool for Dart / Flutter, to offload CPU-bound and heavy I/O tasks to Isolate or Web Worker threads.
https://pub.dev/packages/squadron
MIT License
79 stars 0 forks source link

Decoding JSON to generic class #26

Closed sabin26 closed 10 months ago

sabin26 commented 10 months ago

Hey, I am trying to parse JSON string to dart object using Model.fromJson method inside a squadron worker.

This should work fine for a given model class (not tested though).

@freezed
class Model with _$Model {
  const factory Model({
    required final int id,
    required final String name,
  }) = _Model;
  factory Model.fromJson(final Map<String, dynamic> json) =>
      _$ModelFromJson(json);
}

@SquadronService()
class MyService {
  @SquadronMethod()
  FutureOr<Model> loadModel(final String data) {
    final json = jsonDecode(data) as Map<String, Object?>;
    return Model.fromJson(json);
  }
}

But what I want to do is parse the JSON string to a generic dart object with something as:

@SquadronService()
class MyService {
  @SquadronMethod()
  FutureOr<T> load({
     required final String data,
     required final T Function(Map<String, Object?>) fromJson,
   } ) {
    final json = jsonDecode(data) as Map<String, Object?>;
    return fromJson(json);
  }
}

Here, I can't pass fromJson method as an argument. It doesn't let the build runner pass with squadron_builder. Previously when I was using dart:isolate, I was able to pass this using SendPort.

My use-case is more complicated than this, but I am stuck by this issue. One way to solve this is by returning the Map<String, Object?> and use fromJson from main thread. However, since I am offloading my task, I wanted to parse the Json to dart object from the worker thread.

d-markey commented 10 months ago

Hello,

I've pushed changes to support your use-case.

Plase note that this will probably not work on Web (I haven't tested, but I guess functions cannot cross Web Worker boundaries. Even if they could, you'd have to implement a marshaler so that your T instances can cross the border too. The overhead might negate the advantage of parsing and hydrating in a separate Web Worker).

Please also note that it may also not work on all platforms -- it's a bit difficult to tell exactly what is sendable across 2 isolates. Some info is available from Dart's doc on isolates as well as the doc for send().

Publishing the update now!

Regards

d-markey commented 10 months ago

The doc for send() also mentions bug http://dartbug.com/36983 / https://github.com/dart-lang/sdk/issues/36983. This means if you provide a closure to the worker, it may end up capturing too many things. If this happens, maybe it can be worked around by passing a top-level function, or a static method / constructor tear-off (which is probably always preferable, anyways).