Bardzo często podczas pisania wszelkiego rodzaju aplikacji, nie tylko tych mobilnych, spotkamy się z sytuacją, gdy w jednym momencie musimy wykonać kilka zadań, które zajmują dłuższy czas. Najczęściej dochodzi do takiej sytuacji, w przypadku pobierania danych z kilku endpointów API w jednej chwili, na przykład podczas synchronizacji danych użytkownika. Zobaczmy, co możemy zrobić, aby ten proces zdecydowanie usprawnić.
Częstym błędem w takich sytuacjach jest wykonywanie wszystkich działań jedno po drugim. W takiej sytuacji nasz kod może wyglądać w sposób następujący:
var responseDart = await http
.get('https://www.googleapis.com/books/v1/volumes?q={dartlang}');
var responseFlutter = await http
.get('https://www.googleapis.com/books/v1/volumes?q={flutter}');
var responseAndroid = await http
.get('https://www.googleapis.com/books/v1/volumes?q={android}');
var responseIos = await http
.get('https://www.googleapis.com/books/v1/volumes?q={ios}');
Widzimy tutaj kolejne żądania API odnośnie książek, każde dotyczące innego hasła. To, co powoduje, że powyższy kod nie jest optymalny, wynika z tego, że za każdym razem, aby wykonać kolejne żądanie, musimy odczekać aż poprzednie się wykona, chociaż w rzeczywistości nie potrzebujemy znać wcześniejszej odpowiedzi, aby wykonać kolejne odpytanie API.
Przyjmijmy w tym przypadku, że:
- pierwsze żądanie wykona się w czasie 200 ms
- drugie żądanie wykona się w czasie 150 ms
- trzecie żądanie wykona się w czasie 300 ms
- czwarte żądanie wykona się w czasie 250 ms
Kurs Poznaj Flutter!
Jesteś programistą i chcesz wejść na rynek aplikacji mobilnych? Lub programujesz już aplikacje na Android lub iOS, ale chcesz sprawić, żeby ten proces był szybki i przyjemny, a przy okazji zaoszczędzić na pracy nawet 50% czasu? Próbowałeś ukończyć jakiś tutorial, ale wszystko wydawało Ci się zbyt zawiłe lub szukasz czegoś w języku polskim? Albo w ogóle chcesz zacząć swoją przygodę z programowaniem i szukasz czegoś co da Ci szybko widoczne wyniki oraz będzie łatwe do nauki?
Wejdź na stronę Poznaj Flutter i dowiedz się więcej!
W tym przykładzie, aby obliczyć czas potrzebny na wykonanie tych wszystkich żądań, wystarczy wszystkie te czasy ze sobą zsumować. Znaczy to, że zostaną one wykonane w czasie 900 ms.
Więc jak to zoptymalizować? Z pomocą tutaj przychodzi funkcja Future.wait()
, której zadaniem jest poczekanie na wykonanie się wszystkich zadań asynchronicznych, oraz zwrócenie listy zawierającej wyniki tych działań. Spróbujmy zobaczyć powyższy przykład napisany z wykorzystaniem Future.wait()
:
var responses = await Future.wait([
http.get('https://www.googleapis.com/books/v1/volumes?q={dartlang}'),
http.get('https://www.googleapis.com/books/v1/volumes?q={flutter}'),
http.get('https://www.googleapis.com/books/v1/volumes?q={android}'),
http.get('https://www.googleapis.com/books/v1/volumes?q={ios}')
]);
var responseDart = responses[0];
var responseFlutter = responses[1];
var responseAndroid = responses[2];
var responseIos = responses[3];
Biorąc pod uwagę wyżej wymienione czasy wykonywania się każdego z żądań, wykorzystanie Future.wait()
sprawi, że wszystkie te żądania wykonają się w ciągu 300 ms. Ten czas tak naprawdę wynika z tego, że dokładnie tyle wynosi najdłuższy czas każdego z tych żądań.
Future.wait()
przyjął listę zadań asynchronicznych, które zostały uruchomione w jednym momencie i oczekuje, aż wszystkie z nich zakończą się sukcesem. W tym przypadku wynikiem jest lista odpowiedzi w kolejności takiej, w jakiej żądania zostały przekazane, a nie w kolejności od najszybciej wykonanego.
Wiadome jest to, że w przypadku wykonywania każdego z tych działań może pojawić się błąd. W przypadku wystąpienia pierwszego błędu pozostałe zadania są anulowane oraz zostaje rzucony wyjątek.
Jednak co zrobić w przypadku, kiedy chcemy znać odpowiedzi żądań, które zakończyły się powodzeniem, a tylko ominąć te, w przypadku których pojawił się błąd? To również jest bardzo łatwe zadanie i wystarczy złapać wyjątek w wykonywanych zadaniach:
var responses = await Future.wait([
http
.get('https://www.googleapis.com/books/v1/volumes?q={dartlang}')
.catchError((e) {}),
http
.get('https://www.googleapis.com/books/v1/volumes?q={flutter}')
.catchError((e) {}),
http
.get('https://www.googleapis.com/books/v1/volumes?q={android}')
.catchError((e) {}),
http
.get('https://www.googleapis.com/books/v1/volumes?q={ios}')
.catchError((e) {})
]);
var responseDart = responses[0];
var responseFlutter = responses[1];
var responseAndroid = responses[2];
var responseIos = responses[3];
Jeśli w powyższym przykładzie, żądanie do API o wyniki dotyczące systemu iOS spowoduje wystąpienie błędu, zmienna responseIos
będzie miała wartość Null
, a te, które zostały wykonane prawidłowo, będą posiadały prawidłowe odpowiedzi.
Jak widać, przy pomocy Future.wait()
możemy w znacznym stopniu przyspieszyć wykonywanie działań asynchronicznych, sprawiając, że zostaną one wystartowane w jednej chwili, a czas wykonywania wszystkich zadań, będzie dokładnie równy czasowi najdłuższego z tych zadań. Ta sztuczka zadziała jednak tylko w przypadku, gdy wynik wcześniejszego żądania nie jest przekazywany do któregoś z kolejnych.