Before Getting Started I Suggest You Understand What's Actually Coroutine Jobs Are, Which I Have Discussed In The Previous Blog In This Series. And Make Sure
That You Have Included Coroutines Dependency If It's Not Included:
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0'
Case Scenario
You May Already Know That When You Have Implemented Respective Codeblock In Your Code It Will Execute Sequentially.
In Other Words, They'll Be Executed One-By-One By Default.
But In Few Cases, You May Need To Start The
Execution Of Multiple Codeblocks At A Time
Where Output || Result Timing May Vary.In Those Case Scenario's You Need To Work Asynchronously With Your Respective Codeblock Which Means You Need To Start Those Codeblock's Execution At A Time.
Asynchronous = Running Multiple Codeblocks At A Time Where Result || Output Will Be Returned || Executed Later On.
Introduction To async
Now, As I Mentioned Earlier That If You Want To Start Executing At A Time Then You Should Work Asynchronously.
In Kotlin, We Have Something Known As
async
Which Is Of Type Deferred From Which We Can Build Our Respective Codeblock Asynchronously.async
Doesn't Return A Job Which Is Also A Coroutine Builder.As You Can See Below That
async
Is Of Type Deferred:
Introduction To await
As I Mentioned Above That
async
Will Help You To Work Asynchronously But It May Stop Other Execution Too🤷🏼♂️.To Avoid That And We'll Be Using
await
Method Withasync
So That Remaining Codeblock Execution Will Be Normal.
In Other Words,
await
Ensures That The Execution Will Go Further Until The Function Is Executed Completely.
Use Case Scenario's
- As You Can See That I Have Created 2 Suspend Functions:
private suspend fun apiCall1() {
}
private suspend fun apiCall2() {
}
- And I'll Be Delaying For 2 Seconds And Returning A String In Both Functions Respectively:
private suspend fun apiCall1(): String {
delay(2000L)
return "Html Is A Programming Language🤭"
}
private suspend fun apiCall2(): String {
delay(2000L)
return "Stay Safe😷"
}
Case 1
Code Without Asynchronous
- I'll Implement Those Suspend Functions In A Coroutine With
GlobalScope
And Withlaunch
Coroutine Builder Through IO Dispatcher:
GlobalScope.launch(Dispatchers.IO) {
Log.d("async-await", apiCall1())
Log.d("async-await", apiCall2())
}
Now If You'll Launch The Application After Successful Build You'll Notice That It Takes 4 Seconds To Log Those Messages Which I Have Given.
It's Because
apiCall1()
AndapiCall2()
Suspend Functions Delays2 Seconds Respectively
Before Returning Those Values.If You Want To Measure The Time Taken To Complete Those Executions We Have Few Classes || Methods. In My Case,
I'll Be Using
measureTimeMillis{...}
Which Returns Us The Time Taken To Complete The Execution Which We Have Implemented In That Particular Method || Class In Milliseconds Format.You Can Use
measureTimeMillis{...}
In A Variable So That You Can Get The Duration Of That Execution:val time = measureTimeMillis{ // Codeblock }
- Now Let's Add Those Suspend Functions In
time
Variable In Order To Get The Time Taken To Complete The Execution.
GlobalScope.launch(Dispatchers.IO) {
val time = measureTimeMillis {
Log.d("async-await", apiCall1())
Log.d("async-await", apiCall2())
}
Log.d("async-await", "Time Taken To Complete-> $time ms.")
}
- And As You Can See That It Took 4 Seconds To Complete:
- It's Because We Are Not Executing Our Code Asynchronously, So It Executes Sequentially By Default.
Case 2
Coding Asynchronously
Make Sure That You Have Added
async{...}
To Work Asynchronously.Coding Asynchronously Doesn't Make Your Codeblock To Cook The Result Primarily, It Makes Sure That Your Particular Codeblock Will Be Executed Primarily Rather Than The Result || Output.
I'll Be Implementing Both Of The Suspend Functions In Two Different Variables With
async
In A Coroutine For Demonstration:
GlobalScope.launch(Dispatchers.IO) {
val apiCall1 = async { apiCall1()}
val apiCall2 = async { apiCall2() }
}
Now, It's The Moment Of Truth🔥.
Let's Implement Our Above Code BLock Where We Are Working Asynchronously In
measureTimeMillis{...}
So That We Can Get The Duration Of The Execution.And Make Sure That You Have Implemented
await()
Method With our Variables So That Remaining Codeblock [If Any] Execution Will Be Normal Without Any Problem(s):
GlobalScope.launch(Dispatchers.IO) {
val apiCall1 = async { apiCall1()}
val apiCall2 = async { apiCall2() }
val time = measureTimeMillis {
Log.d("async-await", apiCall1.await())
Log.d("async-await", apiCall2.await())
}
Log.d("async-await", "Time Taken To Complete-> $time ms.")
}
- And As You Can See That Both Of The Suspend Functions Are Executed Simultaneously, Which Means Execution Is Now Completed In 2 Seconds Itself:
- Well, As You Can See That It Worked Asynchronously🤙🏼.
Tips [Kinda]
As I Mentioned Earlier That
async
Is Also A Coroutine Builder That Means You Can Useasync
Instead Oflaunch
If You Want To Work Asynchronously. But The Example I Have Given Above Doesn't Make Sense If I Launch A Coroutine Withasync
.launch
andasync
handle exceptions differently. Since async expects An Eventual Call To await, It Holds Exceptions And Rethrows Them As Part Of The await Call. This Means If You Use async To Start A New Coroutine From A Regular Function, You Might Silently Drop An Exception. These Dropped Exceptions won't appear In Your Crash Metrics or Be Noted In Logcat.
In Other Words,
- Make Sure That You Are Handling
async
Properly Because If You Aren't Handling It Properly Errors May Occur.
Well, That's All For Now🙌
Bye🤗