Dart Programming - Async
Asynchronous Programming Dart Asynchronous Programming in Dart
Flutter is getting more popular than ever! But before you start learning the framework, you need to learn the programming language first. I know that's annoying and it seems like a waste of time. But, that's the challenge that every developer must face.
Dart is a powerful programming language by Google that aims to help developers to build web applications for both client and server, and now mobile apps with Flutter. Just like JavaScript, Dart is an asynchronous programming language. It means that you can run a piece of code without blocking any other tasks, even though, it runs in a single thread.
Dart also use something called an event loop to make the asynchronous code works. Actually, you don't need to really care about this because the Dart VM is doing everything for you, but at least you guys understand how it works and why does it exist.
Future
One of the basic implementation of asynchronous coding pattern in Dart is Future. If you already familiar with JavaScript, Future have the exact same concept with Promise. Basically, you have a piece of code that you want to run whenever some event is triggered.
Once your function gets a Future, you need to decide what do you want to do when the Future is not completed yet, what do you want to do when it's completed, and what do you want to do when it's completed with an error.
How Future Works.
Take a look at this code below.
void fetchPost() {
http.get('https://jsonplaceholder.typicode.com/posts/1')
.then((response) {
print(response);
});
}
In this example, we have a function that use the http
library make an http request from an API. The http.get
method returns a Future. So, we use then
to register a callback that will be triggered when the request is completed.
When you run this function, the event loop catch it, and system started to make a request to the server. While waiting for the response from the API, the event loop still doing its job. Maybe some other events come in, or user do some stuff, your Future still waiting for either a data or an error.
When the data arrives, the callback that we register with then
method gets triggered. Now, your piece of code get executed and print the response to the console.
Handling Errors
Now we know that we can use the then
method to tell the Future what we want to do when the data arrives successfully. But, how about if the there is something wrong in our request like; connection is interrupted, or the form data is not valid?
Well, we can use the catchError
method to tell Dart what we want to do when the Future is throwing an error.
void fetchPost() {
http.get('https://jsonplaceholder.typicode.com/posts/1')
.then((response) {
print(response);
})
.catchError((error) {
print("Something went wrong: ${error.message}");
});
}
catchError
callback gets an error as the first parameter. So, you know what's exactly went wrong.
So far so good! Now I'm going to show you how to make your own instance of the Future.
Create Our Own Future
Most of the time, you don't need to create your own Future. That's because many third-party libraries already generates a Future for you. For instance, network library, accessing device camera, and many other. But still, you can build your own Future by using the Future
constructor.
Let me show you what I'm talking about.
import 'dart:async';
void main() {
final myFuture = Future(() {
print("Hello from the future!");
return true;
});
print("Done!");
}
If you run this code, you will see that the "Done!" string is printed first. Why? because the code inside our Future runs asynchronously. If you already have the value of the Future, you can just use the Future.value
method to return a Future with the given value like the example below. Sometimes it's very helpful for testing.
final myFuture = Future.value("Hello from the future!");
Or, if you want to return an error, you can just use the Future.error
method.
final myFuture = Future.error(Exception());
Async & Await
Most programming languages have async
and await
in their syntax. Basically, their are just an alternate syntax to handle asynchronous code like Future and Streams to make it looks cleaner and readable. Let me gives you an example.
void fetchPost() {
http.get('https://jsonplaceholder.typicode.com/posts/1')
.then((response) {
print(response);
})
}
Remember this piece of code right? Well, this function looks OK. But actually, this code will look a lot messier if we want to use more than one asynchronous function.
First of all, we need to add async
keyword to the function you want to use await. If you don't, the Dart compiler will give you a syntax error.
void fetchPost() async { // ...
}
Then, add the await
keyword to the asynchronous function that returns a Future and store the result value inside a variable like below.
void fetchPost() async {
final result = await http.get('https://jsonplaceholder.typicode.com/posts/1');}
Finally, you can directly print the result value to the console right after the request function.
void fetchPost() async {
final result = await http.get('https://jsonplaceholder.typicode.com/posts/1');
print(result);}
The only thing to keep in mind is you only can use await
keyword inside async
function. That's it!
The big difference by using async & await and not using it is when we want to handle more than one Future at the same time
Using async & await:
import 'dart:convert';
import 'package:http/http.dart';
void fetchUserPosts(token) async {
// Fetch user data
final userResponse = await http.get('https://yourapi.com/user?token=$token');
final user = json.decode(userResponse.body);
// Fetch posts data
final postsResponse = await http.get("https://yourapi.com/posts?userId=${user['id']}");
final userPosts = json.decode(postsResponse.body);
print(userPosts);
}
Using then & catch:
import 'dart:convert';
import 'package:http/http.dart';
void fetchUserPosts(token) {
http.get('https://yourapi.com/user?token=$token')
.then((userResponse) {
final user = json.decode(userResponse.body);
http.get("https://yourapi.com/posts?userId=${user['id']}")
.then((postsResponse) {
final userPosts = json.decode(postsResponse.body);
print(userPosts);
});
});
}
10Asynchronous Programming
You’ve come a long way in your study of the Dart programming language. This chapter is an important one, as it fills in the remaining gaps needed to complete your apprenticeship. In it, you’ll not only learn how to deal with code that takes a long time to complete, but along the way, you’ll also see how to handle errors, connect to a remote server and read data from a file.
Your computer does a lot of work, and it does the work so fast that you don’t usually realize how much it’s actually doing. Every now and then, though, especially on an older computer or phone, you may notice an app slow down or even freeze. This may express itself as jank during an animation: that annoying stutter that happens when the device is doing so much work that some animation frames get dropped.
Tasks that take a long time generally fall into two categories: I/O tasks, and computationally intensive tasks. I/O, or input-output, includes things like reading and writing files, accessing a database, or downloading content from the internet. These all happen outside the CPU, so the CPU has to wait for them to complete. Computationally intensive tasks, on the other hand, happen inside the CPU. These tasks may include things like decrypting data, performing a mathematical calculation, or parsing JSON.
As a developer, you have to think about how your app, and in particular your UI, will respond when it meets these time-consuming tasks. Can you imagine if a user clicked a download button in your app, and the app simply froze until the 20 MB download was complete? You’d be collecting one-star reviews in a hurry.
Thankfully, Dart has a powerful solution baked into the very core of the language that allows you to gracefully handle delays without blocking the responsiveness of your app.
Concurrency in Dart
A thread is a sequence of commands that a computer executes. Some programming languages support multithreading, which is running multiple threads at the same time, while others do not. Dart, in particular, is a single-threaded language.
“What? Was it designed back in 1990 or something?”
No, Dart was actually created in 2011, well into the age of multicore CPUs.
“What a waste of all those other processing cores!”
Ah, but no. This choice to be single-threaded was made very deliberately and has some great advantages as you’ll soon see.
Parallelism vs. concurrency
To understand Dart’s model for handling long-running tasks, and also to see why the creators of Dart decided to make Dart single-threaded, it’s helpful to understand the difference between parallelism and concurrency. In common English, these words mean approximately the same thing, but in computer science, there’s a distinction.
Dart isolates
Dart’s single thread runs in what it calls an isolate. Each isolate has its own allocated memory area, which ensures that no isolate can access any other isolate’s state. That means that there’s no need for a complicated locking system. It also means that sensitive data is much more secure. Such a system greatly reduces the cognitive load on a programmer.
But isn’t concurrency slow?
If you’re running all of a program’s tasks on a single thread, it seems like it would be really slow. However, it turns out that that’s not usually the case. In the following image, you can see tasks running on two threads in the top portion, and the same tasks running on a single thread in the bottom portion.
Comments
Post a Comment