I have the following code to make calls to an API to get my data and then parse the results. It's my understanding that "async" on the forEach loop is NOT blocking, hence my use of an input bool to set if I want these to be blocking calls or not.

My issue now is that I'm hitting a "socket: too many open files" error on some of my calls presumably because I've hit a limit in flutter for too many things waiting on the API http responses (let me know if you need more details of the "tx" functions)

So my question is this. Is there a quick way to throttle the non-blocking "forEach" loop below, and maybe do an await on future.Delayed(Duration(milliseconds:250)) say for every 5th time through the loop?

Of course if I just count and await the async call in the below code I think that the forEach loop will keep right on going through the rest.

Do I have to make a temporary list of all the entries and do them in non blocking batches?

    if (makeBlocking) {
      for (var r in _commissionData[gwSerial].rooms.entries) {
        Map<String, dynamic> res = await _getSetupRoomDevicesApi.tx(
            _token, _clientId, gwSerialString, r.value.id);
        Map<String, dynamic> res2 = await _listCommissionAssociations.tx(
            _token, _clientId, gwSerialString, r.value.id);
        processCommissionData(r.value, res, res2);
      }
    } else {
      if (_commissionData != null) {
        _commissionData[gwSerial]?.rooms?.forEach((roomId, r) async {
          Map<String, dynamic> res = await _getSetupRoomDevicesApi.tx(
              _token, _clientId, gwSerialString, r.id);
          Map<String, dynamic> res2 = await _listCommissionAssociations.tx(
              _token, _clientId, gwSerialString, r.id);
          processCommissionData(r, res, res2);
        });
      }
    }


Solution 1: Eradicatore

Ok, here is what I ended up doing. I created a small function that can be await'ed on if blocking, or not if not blocking. Then just use a simple for loop.

The issue I had was iterating over a map and naturally using a "forEach" was making it not blocking. But I wanted to add a pause (i.e. throttle) every so often in that non-blocking loop. But that would mean using a for loop but I couldn't make the for loop body "async".

Maybe I'm not doing this still the "best" way, so feedback welcome:

  Future<void> syncGatewayRoomDeviceDetailData(
      int gwSerial, bool makeBlocking) async {
    if (_clientId == 0) {
      setState(() {
        _lastCommissionRoomUpdate = DateTime.now();
      });
    } else {
      if (DateTime.now().difference(_lastCommissionRoomUpdate).inMilliseconds >
          -1) {
        print("Sync Gateway:$gwSerial blocking:$makeBlocking");
        _lastCommissionRoomUpdate = DateTime.now();
        String gwSerialString =
            gwSerial.toRadixString(16).padLeft(8, '0').toUpperCase();

        if (makeBlocking) {
          for (var r in _commissionData[gwSerial].rooms.entries) {
            await getAndProcessCommissionData(r, gwSerialString);
          }
        } else {
          // Even if not blocking, still need to throttle
          var throttleCount = 0;
          for (var r in _commissionData[gwSerial].rooms.entries) {
            if (++throttleCount % 9 == 0) {
              await Future.delayed(Duration(milliseconds: 500));
            }
            getAndProcessCommissionData(r, gwSerialString);
          }
        }
      } else {
        print("sync delayed: $gwSerial ");
      }
    }
  }

  Future<void> getAndProcessCommissionData(var r, String gwSerialString) async {
    Map<String, dynamic> res = await _getSetupRoomDevicesApi.tx(
        _token, _clientId, gwSerialString, r.value.id);
    Map<String, dynamic> res2 = await _listCommissionAssociations.tx(
        _token, _clientId, gwSerialString, r.value.id);
    processCommissionData(r.value, res, res2);
  }