deekay42
deekay42

Reputation: 636

How do I await end result of async chain from elsewhere in my code?

Flutter newbie here. I'm trying to parse some json in dart and am getting confused with the async nature of reading the underlying file.

How do I wait until the json has finished parsing and not just the underlying file? My confusion is that reading the file is asynchronous, but parsing the json is synchronous. Now I know that you can use await to wait for the Future to complete that's returned by loadString when reading the file. But how do I then "await" the completion of parsing the json?

jsonDecode does not accept a Future as its arguments and it runs synchronously so I can't wrap it's results into a Future(or can I?). If I could somehow get a Future< Map> _string2Item; as a result of the json parsing operation then that would solve my problem I think since I could simply: await _string2Item before doing anything.

class ItemsRepository
{
 Map<String, Item> _string2Item;

  //This does not work unfortunately. However, I don't want to return an instance that hasn't finished parsing the json
  ItemsRepository() async
  {
    await init();
  }

  Future<String> _loadFile() async {
    return await rootBundle.loadString('assets/data/item_int2string.json');
  }

  Future<void> init() async
  {
    var _fileContents = _loadFile();
    _string2Item = jsonDecode(await _fileContents);
  }

  Item getItem(String id)
  {
    return _string2Item[id];
  }
}

//... somewhere else in my code
ItemRepository ir = ItemRepository();
ir.getItem("42"); //this crashes because _string2Item hasn't finished parsing yet.

Any help is appreciated.

Upvotes: 0

Views: 1235

Answers (1)

Richard Heap
Richard Heap

Reputation: 51692

Use:

ItemRepository ir = ItemRepository();
await ir.init();
ir.getItem('42');

Your class can be written more succinctly as:

class ItemsRepository {
  Map<String, Item> _string2Item;

  Future<void> init() async {
    _string2Item = jsonDecode(
        await rootBundle.loadString('assets/data/item_int2string.json'));
  }

  Item getItem(String id) => _string2Item[id];
}

You could remove the whole class and replace it with a function:

Future<Map<String, Item>> getItems() async => jsonDecode(
      await rootBundle.loadString('assets/data/item_int2string.json'),
    );   

and use it like this

  Map<String, Item> items = await getItems();
  print(items['42']);

Upvotes: 1

Related Questions