initialize static method

Future<DownloadProcess> initialize()

Create a DownloadProcess instance.

Note

  • We do this in a static method as constructors can't be async.

Implementation

static Future<DownloadProcess> initialize() async {
  // Completer as lock to ensure that the spawned Isolate's SendPort is not used before it's
  // exchanged with the main Isolate.
  //
  // NOTE: (ReceivePort, SendPort) is a `Record` type, you might want to look it up on dart.dev.
  //
  // NOTE: A Completer is a Future that can be completed manually, this is important when you
  // consider the async priority order in Dart as follows:
  //
  //    1. Sync  Task                     > Synchronous code, executes first
  //
  //    2. Micro Task                     > high priority async code, executes after sync code
  //                                      usually for things like updating app state, created via
  //                                      `ScheduleMicrotask` / `Completer` / etc.
  //
  //    3. Macro Task / Event Loop Task   > low priority async code, executes after microtasks
  //                                      usually for things like I/O & http requests, created via
  //                                     `Future` / `Stream` / etc.
  //
  // NOTE: A Completer.sync() is essentially a task that is sent to the top of the microtask
  // queue, a.k.a an async task  executed as soon as possible (potentially even synchronously).
  // We use this here to minimize Isolate initialization time.
  final connectionLock = Completer<(ReceivePort, SendPort)>.sync();

  // Create a RawReceivePort (as we can set a different handler when we convert it to a
  // ReceivePort later) and add in the connectionLock logic.
  final initializationPort = RawReceivePort();
  initializationPort.handler = (toSpawnedIsolatePort) {
    connectionLock.complete(
      (
        ReceivePort.fromRawReceivePort(initializationPort),
        toSpawnedIsolatePort as SendPort,
      ),
    );
  };

  // Spawn worker Isolate, on literally any error close the initializationPort and rethrow.
  try {
    var _ = await Isolate.spawn(_isolateSetup, initializationPort.sendPort);
  } on Object {
    initializationPort.close();
    rethrow;
  }

  // Wait for the connectionLock to complete, then return the ReceivePort and SendPort.
  final (ReceivePort fromIsolatePort, SendPort toIsolatePort) = await connectionLock.future;

  // Create a Downloader instance and return it.
  return DownloadProcess._(toIsolatePort, fromIsolatePort);
}