-
Notifications
You must be signed in to change notification settings - Fork 22
How asynchronous programming works
Devrath edited this page Oct 8, 2021
·
8 revisions
- User Interface is something an end-user uses to interact with the system.
- Using this mechanism the end-user interacts with the mobile and communicates with it to perform certain tasks.
- Once the tasks get completed, we show the result to the end-user.
- If the task that is being performed is taken a lot of time, we need to show feedback to the end-user that a task is in progress, otherwise the user thinks that the application is not responding, or worse it doesn't work at all.
- Once good example is that, say we are uploading a photo to the server and this photo is very large so naturally it takes time to upload the photo.
- Since it is taking a lot of time we need to show some feedback to users that an uploading task is in progress.
- We can do this by showing a loader to the end-user and once the uploading is done, we close the loader.
fun uploadImageToServer(image: Image) {
showLoader() // --- > This triggers spinning of loader and starts displaying loader
service.upload(image) // --- > Here the data is sent as bytes to server
hideLoader() // --- > This triggers spinning of loader and stops displaying loader
}
- Here in this simple function
uploadImageToServer
lot of things happen -
showLoader()
calls the loader to display, Now let's assume we are calling the android loader, this is not just a call it's an android functionality built that shows an animation. But so it's doing some operation. - If
service.upload
need to wait untilshowLoader()
gets completed theservice.upload
becomes a blocking call. - Similarly
hideLoader()
functionality to stop the above loader - So here the concept of multi-threading comes to the picture, If we have many threads that perform tasks parallel, We can do more things at once.
- In the above example, We show the loader in
UI-thread
and theservice call
, we perform in a background thread doing so we need not need to wait for individually things to complete to continue to next instead, We can do things parallel.
- We use the main thread to manage the UI
- Each application has only one thread called
main thread
to avoid the deadlock. - We use the many threads to achieve what we called concurrency with the help of multi-threading.
- Understanding how threads communicate is the key to knowing concurrency.
- All threads should be able to communicate between them seamlessly without causing problems.
- Understanding how threads communicate is key in knowing how
concurrency
happens between the threads. -
Communicating
between the threads is a challenge, it is the key to the challenge of achieving a well-written concurrent code. - There needs to be proper synchronization.
- Say you are displaying the
loader
, so we display multiple frames of images one after another -> Now If before one image is displayed another image replaces it, There is a loss in the frame. So we need a data structure that manages a thread-safe condition meaning thedata structure
should work correctly even though multiple threads access it. - Accessing one data from multiple places maintaining concurrency and performance is not easy.
- Say you are displaying the
- We can use
queue
as a good example for thread safety. - Threads communicate using the
queue
asproducers
andconsumers
, Theproducer
is the one who puts the data into the queue and theconsumer
is the one who takes the data from the queue. - Data structure we call it as
first in first out
. Threads put the information to queue as messages thus encapsulating it. - Using this queue system. The consumer can wait until the message becomes available and if the thread is blocking the queue the consumer can block the queue and just try on after some time.
- A good example we can take is, say there is a queue of customers in the food delivery shop KFC, Now if the food is available the consumer thread that is the person that servers the food at the counter give the food, now if food is not available the consumer will make the customers wait in the queue and block the queue so customers wait, later once the food is prepared, he unblocks the queue.
- One challenge we face in asynchronous programming is callback nesting.
-
Consider a scenario, We upload an image to server :
show the spinner
--then-->load the image
--then-->resize the image
--then-->upload the resized image
--then-->hide the spinner
- Each step has success and failure blocks, and they have to be nested inside another, we can observe a lot of nested levels here itself.
Drawback of using the nested way is passing data from one to another leading to nested levels and hard to maintain code
-
Consider a scenario, We upload an image to server :
- Using
rx
or the reactive extensions, we can have asynchronous functions wrapped in the stream of events. - Rx provides useful constructs called operators and uses the observer pattern
- You can subscribe to a stream of events map, filter, reduce, combine them using the chain of operators with a single lambda event.
Callback Sample
fun uploadImage(imagePath: String) {
showLoadingSpinner()
loadImage(imagePath) { image ->
resizeImage(image) { resizedImage ->
uploadImage(resizedImage) {
hideLoadingSpinner()
}
}
}
}
Callback Sample transformed to reactive sample
fun uploadImage(imagePath: String) {
loadImage(imagePath)
.doOnSubscribe(::showLoadingSpinner)
.flatMap(::resizeImage)
.flatMapCompletable(::uploadImage)
.subscribe(::hideLoadingSpinner, ::handleError)
}
- In the modified
rx
sample, we can observe that a stream of data is being modified by a bunch ofrx operators
- Data is passed from
loadImage()
---> to another function creating new stream ----> Then the modified stream is passed to another function calledresizeImage()
---> Then now yet again the modified stream is passed touploadimage()
where the image is uploaded to server ---> These stream is not executed until someone subscribes to them ---> When someone subscribes to themdoOnSubscribe()
is executed. - One more advantage here is, If there is an exception in the stream at any point here, It is not thrown straight away instead it's propagated down the stream and handled at one place.
- Even though it solves these problems associated with the traditional approach, the
rx
brings a lot more complexity to the table things likeobserver design pattern
,knowing about streams
,should we need to consider clicks also as streams or not
,it can be simpler and then why to make it so complex
,learning curve is high here
.
A simpler way to deal with synchronicity is by using coroutines