Moti Bartov
Moti Bartov

Reputation: 3592

How to work with async* functions in Dart

I am using flutter_bloc library.

In the bloc, the mapEventToState method is an async* function which returns Stream<BlocState>. From this function I am calling other async* functions like this yield* _handleEvent(event)

In such method, I am calling some Future returns functions but in the Future then() function it wont let me call other yield* functions.

Here is an example:

Stream<BlocState> mapEventToState(BlocEvent event) async*{
     yield* _handlesEvent(event); //This calls to worker method 
}

Stream<BlocState> _handleEvent(BlocEvent event) async* {
   _repository.getData(event.id).then((response) async* { //Calling Future returned function
         yield* _processResult(response); //This won't work
     }).catchError((e)  async* {
         yield* _handleError(e);  //This won't work either
     });

   Response response = await _repository.getData(event.id); //This do works but I want to use it like above, is it possible?
   yield* _processResult(response); //This do works
}

The question is however, how to combine between Future and Stream in dart. I could use await _repository.getData which works. but then I won't catch the error.

Upvotes: 1

Views: 1839

Answers (3)

jamesdlin
jamesdlin

Reputation: 90175

  1. await is just syntactic sugar for .then(), and putting await in a try-catch block is syntactic sugar for using .catchError. Things that you can do one way can be done with the other.

  2. In your first version that uses .then()/.catchError(), your function doesn't return anything.

  3. Your callbacks won't work because you're using yield* in them, but you haven't specified the callbacks with sync* or async*. To avoid name collisions, the yield keyword requires them (in the same way that await requires a function use async or async*).

Here's a version that should work with .then() and .catchError():

Stream<BlocState> _handleEvent(BlocEvent event) async* {
  yield* await _repository.getData(event.id).then((response) async* {
    yield* _processResult(response);
  }).catchError((e) async* {
    yield* _handleError(e);
  });
}

Note that the callbacks don't need to use yield*; they could just return their Streams directly:

Stream<BlocState> _handleEvent(BlocEvent event) async* {
  yield* await _repository.getData(event.id).then((response) {
    return _processResult(response);
  }).catchError((e) {
    return _handleError(e);
  });
}

But (as everyone else has noted) using await instead of the Future API simplifies the whole thing (especially since we're already using await anyway):

Stream<BlocState> _handleEvent(BlocEvent event) async* {
  try 
    response = await _repository.getData(event.id);
    yield* _processResult(response);
  } catch (e) {
    yield* _handleError(e);
  }
}

See https://dartpad.dartlang.org/fc1ff92e461754bdb35b998e7fbb3406 for a runnable example.

Upvotes: 1

Łukasz Olszewski
Łukasz Olszewski

Reputation: 915

To handle errors in an async function, use try-catch:

try {
  Response response = await _repository.getData(event.id)
} catch (err) {
  print('Caught error: $err');
}

Upvotes: 0

Candace
Candace

Reputation: 81

Try using a try-catch block instead. It works for me with await operations.

Upvotes: 1

Related Questions