Is it possible to call multiple APIs at one time

Viewed 1112

I'm integrating some API in flutter, but I'm stuck. At one point on API, I need to pass data for the past 15 days, so on that basis I get a result. The issue is the loading time is too long now because I use a for loop and call one API, then the second API, then third and so on. What I'm looking for is can I call my 15 APIs together?

My code

  getAllEvent() async {
    final storage = new FlutterSecureStorage();

    String asset_id = await storage.read(key: "asset_id");

    var toDate = DateTime.now();
    var fromDate = toDate.subtract(Duration(days: 1));
    for (var i = 0; i <= 15; i++) {
      // put them into a string
      final fromDateString =
          '${fromDate.year}-${fromDate.month}-${fromDate.day}';
      final toDateString = '${toDate.year}-${toDate.month}-${toDate.day}';
      final f = new DateFormat('yyyy-MM-dd');
      fromDate = fromDate.subtract(Duration(days: 1));
      toDate = toDate.subtract(Duration(days: 1));

      final url =
          'https://apihide/${asset_id}?start=${f.format(fromDate)}&end=${f.format(toDate)}';
      // //print(url);
      // //print(ApiToken);
      http.Response res = await http.get(
        url,
        headers: <String, String>{'x-access-token': ApiToken},
      );
      var data = json.decode(res.body.toString());
      if (data['count'] > 0) {
        eventList.eventListData.addAll(data['items']);
      }
    }

    setState(() {
      eventDataLoaded = true;
      checkDataLoaded();
    });
  }

You can see in code by for loop API is calling one by one. I need to call it 15 times in a single shot. (There is no issue in API if I hit 15 times at once because the API provider says to do this.)

3 Answers

you can use Future.wait(listOfFutures); which takes a list of futures, and returns a List<Response>

for example:


final results = await Future.wait([
  http.get('url1'),
  http.get('url1'),
  http.get('url1'),
]);

print(results[0].statusCode);
print(results[0].body);
.
.
.


You'll need to combine both previous answers; those have a bit too much hardcoding and repeated code.

Here's my solution, see if it works for you:

getAllEvent() async {
    final storage = new FlutterSecureStorage();
    
    //Store your Future responses here
    var responses=<Future<http.Response>>[];

    String asset_id = await storage.read(key: "asset_id");

    var toDate = DateTime.now();
    var fromDate = toDate.subtract(Duration(days: 1));
    
  for (var i = 0; i <= 15; i++) {
      // put them into a string
      final fromDateString =
          '${fromDate.year}-${fromDate.month}-${fromDate.day}';
      final toDateString = '${toDate.year}-${toDate.month}-${toDate.day}';
      final f = new DateFormat('yyyy-MM-dd');
      fromDate = fromDate.subtract(Duration(days: 1));
      toDate = toDate.subtract(Duration(days: 1));

      final url =
          'https://apihide/${asset_id}?start=${f.format(fromDate)}&end=${f.format(toDate)}';
      // //print(url);
      // //print(ApiToken);

      // This is where a future will be added to the list. Notice there is no await keyword here, so you are not waiting for the response
      responses.add(http.get(
        url,
        headers: <String, String>{'x-access-token': ApiToken},
      ));
    }
    
    var results=await Future.wait(responses);
    
    for (var res in results) {
      var data = json.decode(res.body.toString());
      if (data['count'] > 0) {
        eventList.eventListData.addAll(data['items']);
      }
    }
    

    setState(() {
      eventDataLoaded = true;
      checkDataLoaded();
    });
  }

Note that this can be further optimized. The problem in the code above is that you are waiting for all APIs to complete, and only then you process all of them. Assuming you don't care in which order the results appear in eventListData, you could be processing the calls as they arrive. Something like this (showing only code from http.get call to setState() call:

      responses.add(http.get(
        url,
        headers: <String, String>{'x-access-token': ApiToken},
      ).then( (res) {
        var data = json.decode(res.body.toString());
        if (data['count'] > 0) {
          eventList.eventListData.addAll(data['items']);
        }
      }));
  
  // closing the for loop here
  }
    
    // you still need to wait the futures to complete before calling setState()
    var results=await Future.wait(responses);
    
//     for (var res in results) {
//       var data = json.decode(res.body.toString());
//       if (data['count'] > 0) {
//         eventList.eventListData.addAll(data['items']);
//       }
//     }

setState(() {
...

And while I'm at it: what if you do care in what order responses are added to eventListData? In that case - you only need to call json.decode in then() - after all this is the slow part that you want to run as soon as possible. You can do something like this:

      
//note that here your responses will have parsed response data
responses.add(http.get(
        url,
        headers: <String, String>{'x-access-token': ApiToken},
      ).then( (res) => json.decode(res.body.toString()));
  
  // closing the for loop here
  }
    
    // you still need to wait the futures to complete before calling setState()
    var results=await Future.wait(responses);
    
     for (var data in results) {
//       no need for this line - already done in then()
//       var data = json.decode(res.body.toString());
       if (data['count'] > 0) {
         eventList.eventListData.addAll(data['items']);
       }
     }

setState(() {
...

To answer your question on @Adnan's answer:

I would separate the building of the url into its own function, then add the urls to a list.

  List<String> urlList = [];

  Future<void> buildUrls() async {
    String url = '';
    for (int i = 0; i < 15; i++) {
      // your url building code here
      urlList.add(url);
    }
  }

Then put your api call in its own function

Future<void> fetchApi(String url) async {
    // http code here
  }

Then call one function that calls both, and passes in each string in the list at the same time

  void getAllEvents() async {
    await buildUrls();
    await Future.wait([
      fetchApi(urlList[0]),
      fetchApi(urlList[1]),
      fetchApi(urlList[2]),
      fetchApi(urlList[3]),
      fetchApi(urlList[4]),
      fetchApi(urlList[5]),
      fetchApi(urlList[6]),
      fetchApi(urlList[7]),
      fetchApi(urlList[8]),
      fetchApi(urlList[9]),
      fetchApi(urlList[10]),
      fetchApi(urlList[11]),
      fetchApi(urlList[12]),
      fetchApi(urlList[13]),
      fetchApi(urlList[14]),
    ]);
  }

buildUrls might not have to be a Future, but I can't test your code since I don't have all the info so I used a Future just to be sure all the urls are build before the http call.

Related