visit
In the realm of Dart programming, there exists a powerful utility known as Future.wait()
. This often-overlooked gem enables developers to orchestrate concurrent execution of multiple asynchronous tasks, leading to improved performance and efficiency.
In this blog post, we will explore the problem domain where Future.wait()
can save the day and delve into best practices for utilizing it effectively.
Given I can’t share the whole code, I will embody this code smell with the power of Future.wait()
later in this blog:
void fetchFromRemoteApis() {
final apiAFuture = fetchDataFromApi('API A');
final apiBFuture = fetchDataFromApi('API B');
final apiCFuture = fetchDataFromApi('API C');
apiAFuture.then((resultA) {
print('Received result from API A: $resultA');
apiBFuture.then((resultB) {
print('Received result from API B: $resultB');
apiCFuture.then((resultC) {
print('Received result from API C: $resultC');
print('All API calls completed successfully!');
}).catchError((errorC) {
print('An error occurred while fetching data from API C: $errorC');
});
}).catchError((errorB) {
print('An error occurred while fetching data from API B: $errorB');
});
}).catchError((errorA) {
print('An error occurred while fetching data from API A: $errorA');
});
}
In this suboptimal approach, the code chained the then
callbacks sequentially for each API call. This results in nested callbacks, commonly known as the "callback hell" or "pyramid of doom" pattern. It leads to less readable and more error-prone code, especially when handling errors.
The code also first fetches data from API A. Upon successful completion, it proceeds to fetch data from API B. Finally, when API B completes successfully, it proceeds to fetch data from API C. At each stage, error handling is implemented using catchError()
to capture and handle any errors that may occur during the API calls.
Harnessing the Power of Future.wait()
: Future.wait()
, is a function that enables concurrent execution of asynchronous tasks. With this mighty tool at our disposal, we can dramatically enhance the efficiency of our code by fetching data from multiple remote APIs concurrently, taking advantage of parallelism and reducing overall execution time.
Here's how Future.wait()
works:
Future
objects, each representing an asynchronous task, such as fetching data from a remote API.Future
objects to Future.wait()
.Future.wait()
returns a new Future
that completes when all the provided Future
objects have completed.Future.wait()
Consider a scenario where your application needs to fetch data from three different remote APIs: A, B, and C. Without Future.wait()
, you might fetch data from A, wait for it to complete, then fetch data from B, and finally fetch data from C. This sequential approach can be time-consuming and inefficient.
However, by utilizing Future.wait()
, you can kick off all three asynchronous tasks concurrently, reducing the overall execution time. The Future
returned by Future.wait()
will complete only when all three API calls have finished, allowing you to collect the results efficiently.
Writing Better Code:
To make the most of Future.wait()
, here are a few best practices:
Use Future.wait()
when the tasks are truly independent and can be executed concurrently. If there are dependencies between tasks, consider using Future.then()
or async/await
to handle them.
Ensure that the tasks passed to Future.wait()
are truly asynchronous and return a Future
. If not, consider wrapping synchronous code in a Future
using Future.value()
or Future.microtask()
.
Utilize error-handling mechanisms like try-catch
or onError
callbacks to handle exceptions that might occur during the execution of concurrent tasks.
If there's a need to set a timeout for the completion of Future.wait()
, you can use Future.wait(...).timeout(...)
to handle situations where certain tasks take an unusually long time.
The Fix:
So here, I embodied the code smell and used Future.wait()
to make it better. I have also chosen open random API endpoints for you to see how effective it can serve concurrent execution:
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
void main() {
fetchFromRemoteApis();
}
void fetchFromRemoteApis() async {
final apiAFuture = fetchDataFromApi('//jsonplaceholder.typicode.com/posts');
final apiBFuture = fetchDataFromApi('//jsonplaceholder.typicode.com/comments');
final apiCFuture = fetchDataFromApi('//jsonplaceholder.typicode.com/todos');
try {
List<dynamic> results = await Future.wait<dynamic>([apiAFuture, apiBFuture, apiCFuture]);
for (var result in results) {
print('Received result: $result');
}
print('All API calls completed successfully!');
} catch (error) {
print('An error occurred: $error');
}
}
Future<dynamic> fetchDataFromApi(String apiUrl) async {
final response = await http.get(Uri.parse(apiUrl));
// Check the response status
if (response.statusCode == 200) {
final jsonResponse = jsonDecode(response.body);
return jsonResponse;
} else {
throw Exception('API request failed with status ${response.statusCode}');
}
}
As much as different software engineers can have differing opinions such as using .map()
with .then()
inside Future.wait()
, which I think is unnecessary nested callbacks, or use only .then()
with Future.wait()
which creates an additional callback function and introduces an extra layer of complexity, I believe the algorithmic complexity of the code smell is reduced drastically by the fix provided in this blog.
In the ever-evolving world of Dart programming, it's essential to leverage the right tools for improved performance and efficiency. Future.wait()
is a valuable utility that empowers developers to achieve concurrent execution of asynchronous tasks, effectively reducing execution time and optimizing resource utilization. By incorporating Future.wait()
into your code, you can unlock the magic of concurrent programming and embrace the power of Dart to its fullest potential.
So, the next time you find yourself juggling multiple asynchronous tasks, remember the spellbinding capabilities of Future.wait()
and watch as your code performs its own brand of magic.
If you need more resources on Future.wait()
, kindly refer to and .