Return multiple results from an async method?

2020-03-31 05:59发布

问题:

I currently have a method that looks something like this:

typedef void MyCallback(int status, String body);


void makeRequest(String url, MyCallback callback) async {
  if( someCondition ){
    callback(1, '');
  }

  Response response = await http.get(url);

  if( response.httpCode == 200 ){
    callback(2, response.body);
  }
  else{
    callback(3, '');
  }
}

I want to get rid of the callback so that I can wait for the result(s) of makeRequest. But if I simply make it return a Future, I won't be able to return more than once.

I looked into using streams, but it seems a bit complicated. So basically I'm looking for something as convenient as a Future but that could receive "partial results", i.e. receive a result more than once. To illustrate:

MultiFuture<int> makeRequest(String url){
  MultiFuture result = new MultiFuture();

  if( someCondition ){
    result.value(1);
  }

  http.get(url).then((response){
    if( response.httpCode == 200 ){
      result.value(2);
    }
    else{
      result.value(3);
    }

    result.finish();
  });


  return result;
}




MultiFuture requestResult = makeRequest('https://example.com');

await requestResult.onValue((int value){
  print('value: $value');
});      
// When result.finish() is called, the await is finished

Is using a stream my best option, or is there some kind of MultiFuture that I just don't know about?

回答1:

Return multiple results from an async method is exactly what streams does. Here is an example I made based on your code so it could be easier for you to understand how it works:

class MultiFuture {
  StreamController<int> _resultController = StreamController<int>();
  Stream<int> get requestResult => _resultController.stream;

  void makeRequest(String request) async {
    if (true) {
      _resultController.sink.add(1);
    }

    // mock request
    await Future.delayed(Duration(seconds: 3));

    if (true) {
      _resultController.sink.add(2);
    } else {
      _resultController.sink.add(3);
    }
  }

  void example() {
    makeRequest("https://example.com");

    requestResult.listen((value) {
      print('value: $value');
    });
  }
}

You can test it calling MultiFuture().example();


But I recommend you to see these simple short videos to have a better idea: Async Coding With Dart