How can I get multiple messages from dart isolate?

451

Compute only returns one result. If you want to pass multiple 'events' back to the main isolate then you need to use the full Isolate logic (with sendPort and receivePort).

For example, the following code runs in an isolate, and downloads a file while emitting float values to represent progress, potentially a String to indicate log messages and then a bool to indicate success or failure upon completion.

  Future<void> isolateDownload(
      DownloadRequest request) async {
    final sendPort = request.sendPort;
    if (sendPort != null) {
      var success = false;
      var errorMessage = '';
      var url = Uri.parse('a_url_based_on_request');
      IOSink? out;
      try {
        http.StreamedResponse response =
            await http.Client().send(http.Request('GET', url));
        if (response.statusCode == 200) {
          var filePath =
              join(request.destinationDirPath, '${request.fileName}.ZIP');
          var contentLength = response.contentLength;
          var bytesLoadedUpdateInterval = (contentLength ?? 0) / 50;
          var bytesLoaded = 0;
          var bytesLoadedAtLastUpdate = 0;
          out = File(filePath).openWrite();
          await response.stream.forEach((chunk) {
            out?.add(chunk);
            bytesLoaded += chunk.length;
            // update if enough bytes have passed since last update
            if (contentLength != null &&
                bytesLoaded - bytesLoadedAtLastUpdate >
                    bytesLoadedUpdateInterval) {
              sendPort.send(bytesLoaded / contentLength);
              bytesLoadedAtLastUpdate = bytesLoaded;
            }
          });
          success = true;
          if (contentLength != null) {
            sendPort.send(1.0); // send 100% downloaded message
          }
        } else {
          errorMessage =
              'Download of ${request.fileName} '
              'received response ${response.statusCode} - ${response.reasonPhrase}';
        }

      } catch (e) {
        errorMessage = 'Download of ${request.chartType}:${request.chartName} '
            'received error $e';
      } finally {
        await out?.flush();
        await out?.close();
        if (errorMessage.isNotEmpty) {
          sendPort.send(errorMessage);
        }
        sendPort.send(success);
      }
    }
  }

The code that spawns the isolate then simply checks for the type of the message passed to it to determine the action.

  Future<bool> _downloadInBackground(
      DownloadRequest request) async {
    var receivePort = ReceivePort();
    request.sendPort = receivePort.sendPort;
    var isDone = Completer();
    var success = false;
    receivePort.listen((message) {
      if (message is double) {
        showUpdate(message);
      }
      if (message is String) {
        log.fine(message); // log error messages
      }
      if (message is bool) {
        success = message; // end with success or failure
        receivePort.close();
      }
    }, onDone: () => isDone.complete()); // wraps up
    await Isolate.spawn(isolateDownload, request);
    await isDone.future;
    return success;
  }
Share:
451
Abhishek Patil
Author by

Abhishek Patil

Updated on January 02, 2023

Comments

  • Abhishek Patil
    Abhishek Patil over 1 year

    How can I get multiple messages from dart isolate?

    I'm trying to create an excel file and want to do some operation on that file in an isolate. Before doing an operation on that file, I want to return an message to main isolate, that excel file is created.

    Here is function goes in isolate :

    foo(String filePath){
        // create excel file
        var bytes = File(filePath).readAsBytesSync();
        var excel = Excel.decodeBytes(bytes);
    
        //HERE I WANT TO SEND THE MESSAGE THAT CREATING EXCEL FILE IS DONE
    
        // some operatoin on excel file
        var result = doSomeOperation(excel);
        return result;
    }
    

    Main isolate code :

    var result = await compute(foo, filePath);
    

    What should I do to get creating file message before the actual result comes?

    For excel, I'm using excel: ^2.0.0-null-safety-3 package.

    • pskink
      pskink over 2 years
      you cannot do that with compute function, instead you need to spawn an Isolate and use SendPort / ReceivePort api
  • Vladimir.Shramov
    Vladimir.Shramov over 1 year
    thanks for explanation Bram. could you specify dependencies used in you code? if possible. I mead "DownloadRequest" and "http."