Search code examples
multithreadingflutterdartdart-isolates

Dart- Isolates are very slow when working with lists


I want to generate a list of 300 items from a String however when this task is divided into 3 isolates,where each isolate is generating a list of 100 items it takes just as long as it would take a single isolate to generate a list of 300 items.

the fetchCoinList() method below is what is being run in the isolate and the generateList() method spawns 3 isolates.

All these isolates do work in parallel and do return their respective lists.

List<CoinData> fetchCoinList(ListConfig listConfig) {
  List<CoinData> coinList = [];

  for (int index = listConfig.listStart; index < listConfig.listEnd; index++) {
    if (jsonDecode(listConfig.listData)['data'][index]['rank'] == null ||
        jsonDecode(listConfig.listData)['data'][index]['priceUsd'] == null ||
        jsonDecode(listConfig.listData)['data'][index]['changePercent24Hr'] ==
            null) continue;

    int rank =
        int.parse(jsonDecode(listConfig.listData)['data'][index]['rank']);
    String name = jsonDecode(listConfig.listData)['data'][index]['name'];
    String symbol = jsonDecode(listConfig.listData)['data'][index]['symbol'];
    String id = jsonDecode(listConfig.listData)['data'][index]['id'];
    double value = double.parse(
        jsonDecode(listConfig.listData)['data'][index]['priceUsd']);
    double percentChange = double.parse(
        jsonDecode(listConfig.listData)['data'][index]['changePercent24Hr']);
    String image =
        'https://static.coincap.io/assets/icons/${symbol.toLowerCase()}@2x.png';
    print(listConfig.listEnd);

    coinList.add(CoinData(rank, id, name, symbol, value, percentChange, image));
  }
  return coinList;
}

Future<List<CoinData>> generateList(String response) async {
  print("Inside isolate");

  List<CoinData> coinList = [];
  List list = [];

  for (int i = 0; i < 3; i++) {
    ListConfig listConfig = ListConfig(response, i * 100, (i * 100) + 100);
    list.add(compute(fetchCoinList, listConfig));
  }

  for (int i = 0; i < 3; i++) {
    coinList = coinList + await list[i];
  }

  return coinList;
}

What could be a workaround for this problem? how would one go about generating a list of 300 or a 1000 items if required(without hanging the main thread)?


Solution

  • Your main issue are probably the kinda horrible way you are handling JSON parsing in fetchCoinList where you are running jsonDecode() for every field you want to extract from your JSON string.

    You should instead cache the result from jsonDecode(). Also, you should also cache some of the other operations you are doing a lot.

    I have made the following example of how I would rewrite the code:

    List<CoinData> fetchCoinList(ListConfig listConfig) {
      final jsonObject = jsonDecode(listConfig.listData) as Map<String, dynamic>;
      List<CoinData> coinList = [];
    
      for (int index = listConfig.listStart; index < listConfig.listEnd; index++) {
        final dataMap = jsonObject['data'][index] as Map<String, dynamic>;
    
        if (dataMap['rank'] == null ||
            dataMap['priceUsd'] == null ||
            dataMap['changePercent24Hr'] == null) continue;
    
        print(listConfig.listEnd);
    
        final symbol = dataMap['symbol'] as String;
    
        coinList.add(CoinData(
            rank: dataMap['rank'] as int,
            name: dataMap['name'] as String,
            symbol: symbol,
            id: dataMap['id'] as String,
            value: double.parse(jsonObject['priceUsd'] as String),
            percentChange: double.parse(jsonObject['changePercent24Hr'] as String),
            image:
                'https://static.coincap.io/assets/icons/${symbol.toLowerCase()}@2x.png'));
      }
      return coinList;
    }
    
    class CoinData {
      final int rank;
      final String name;
      final String symbol;
      final String id;
      final double value;
      final double percentChange;
      final String image;
    
      CoinData({
        required this.rank,
        required this.name,
        required this.symbol,
        required this.id,
        required this.value,
        required this.percentChange,
        required this.image,
      });
    }