1

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)?

julemand101
  • 28,470
  • 5
  • 52
  • 48
Hamza Baig
  • 53
  • 6
  • Please don't create multiple questions on the same topic. You already posted: https://stackoverflow.com/questions/70730674/flutter-main-thread-still-hangs-even-when-running-heavy-computations-on-sepera – julemand101 Jan 17 '22 at 10:58
  • *"how would one go about generating a list of 300 or a 1000 items if required(without hanging the main thread)?"* - so now you are calling `compute` function three times and yet the main thread hungs? – pskink Jan 17 '22 at 10:58
  • When that said. It is not clear in your example how you are calling these methods from your main-isolate. – julemand101 Jan 17 '22 at 10:59
  • @pskink The main thread does not hang in this case however it takes a long time for isolates to generate their respective lists...if I were to generate a list of 300 items on the main thread it would generate it in a significantly less amount of time but it will cause the ui to freeze for that amount of time. – Hamza Baig Jan 17 '22 at 11:06
  • it it normal: spawning an `Isolate` and passing your data two-ways is not a zero cost action - it takes a while – pskink Jan 17 '22 at 11:08
  • @julemand101 the generateList() function is being called in the main thread via an onPress() event which is async and uses the await keyword before generateList() – Hamza Baig Jan 17 '22 at 11:09
  • @pskink is there a better way to generate large lists(without hanging the main thread) ?because the multiple isolate approach is futile as doing so takes longer than the main thread. – Hamza Baig Jan 17 '22 at 11:11
  • @pskink I did indeed tried that approach but the results were the same – Hamza Baig Jan 17 '22 at 11:13
  • @pskink it doesnt take very long but it defeats the purpose of using multiple threads in my application as the time taken for 3 threads is the same as it would take for 1 thread. I was thinking of generating a list of 1000-2000 items but as I can observe from this approach it would take significantly longer as well. – Hamza Baig Jan 17 '22 at 11:16
  • 1
    It should also be noted that `fetchCoinList` is really not written in any efficient way at all. It is actual kinda horrible that you are calling `jsonDecode` every time you need a element from the data structure instead of saving the result of `jsonDecode`. – julemand101 Jan 17 '22 at 11:31
  • Similar, you should save the result of `jsonObject['data'][index]` if you are going to use the elements from this element multiple times like you are doing. – julemand101 Jan 17 '22 at 11:31
  • @julemand so I should convert the result of jsondecode into a list or iterable and use that to generate my objects? – Hamza Baig Jan 17 '22 at 11:40
  • @HamzaBaig Give me a few minutes to write an example. – julemand101 Jan 17 '22 at 11:43
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/241141/discussion-between-julemand101-and-hamza-baig). – julemand101 Jan 17 '22 at 11:49

1 Answers1

1

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,
  });
}
julemand101
  • 28,470
  • 5
  • 52
  • 48
  • 2
    implementing this solution did improve the performance significantly which made it so I only needed to use one seperate isolate only... Thanks. – Hamza Baig Jan 17 '22 at 12:56