Coroutine Jobs In Kotlin
Tags: kotlin, android, coroutines
Published: 2021-07-03 12:17:06

Before Getting Started I Suggest You Understand What's Actually A runBlocking() Is, Which I Have Discussed In The [Previous Blog](https://dev.to/saketh/runblocking-in-kotlin-coroutines-5a7l) 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'
```
->Whenever We Launch A Coroutine **It Returns A Job**. In Other Words, It **`Returns The Work Which We Have Assigned `**. Ex:- Loading Data, Network Operations, Working With UI, Some Background Stuff Etc., 

->These Job(s) || Work Which You Have Assigned In Coroutines Can be Saved In A Variable Too:
```
val work = GlobalScope.launch(Dispatchers.Main) {
            Log.d("jobs-.join()", "Started Execution From GlobalScope Which Is In A Variable")
}
```
-> Which May Look Like This In Your Android Studio || Other IDE Which You Use:

![assginging a variable.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1625314379526/MYGF4PYLy.png)

## Jobs:
-> Now In Case If You Want To Finish This Particular Variable's Task Primarily Rather Than Other Task(s) Then **`Jobs`** May Help You.

->Before Getting Started I Want To Clear You That **`Jobs`** Is  **Not** A Function. We Have Multiple Suspend Functions And Other CodeBlocks Which Can Make Our Job Easy😉:

- .**join()**

- .**cancel()**


->Now, These Suspend Functions And Other CodeBlocks Can Be Defined As Jobs Through Coroutine Builders. 

- Let's Go In-Deep To Check What Actually These Functions Do And How Can Be Used And When These Can Be Used.

- I'm Going To Use *runBlocking()* Which I Have Discussed In The [Previous Blog](https://dev.to/saketh/runblocking-in-kotlin-coroutines-5a7l), So That We'll Get An Idea How & When These Functions Can Be Used.

- **runBlocking()** Blocks Whole Thread Until The Execution Of **runBlocking()** Completes.

-> If You **Don't** Want To Execute That Variable Primarily Or Don't Want To Execute In `runBlocking()`, You Can Execute That Variable In A Coroutine As Well:

### .join():

> As Name Suggests, Our Job||Work Can Join Some other Suspend Function Or Coroutine.

- Whenever We Have Assign `.join()` Or Other Jobs In **runBlocking()** The Execution Of Variable Completes First And Then Back To The Normal State Of Our Code.

-> That Mean When Ever We Have Assigned `.join()` Or Other Jobs To A Variable Where We have Stored Our Coroutine Execution Will Primarily `Completes First` And Then Remaining Execution Of Other Code Block Will Be Continued. [If You Are Working With `runBlocking()`]

->As You Can See That I Have Added Multiple Coroutines With Individual Coroutine Builders Where I Have Assigned One Of The Coroutines In A Variable Named **`work`** To Demonstrate:

```
val work = GlobalScope.launch(Dispatchers.Main) {
            Log.d("jobs-.join()", "Started Execution From GlobalScope Which Is In A Variable")
        }
        GlobalScope.launch(Dispatchers.Default) {
            delay(2000L)
            Log.d("jobs-.join()", "Running From ${Thread.currentThread().name}")
        }
        GlobalScope.launch(Dispatchers.IO) {
            delay(2000L)
            Log.d("jobs-.join()", "Running From IO Coroutine")
        }
```

**Case1:**

- If `.join()` Is Executed In **`runBlocking()`**:

```
 val work = GlobalScope.launch(Dispatchers.Main) {
            Log.d("jobs-.join()", "Started Execution From GlobalScope Which Is In A Variable")
        }
        GlobalScope.launch(Dispatchers.Main) {
            delay(2000L)
            Log.d("jobs-.join()", "Running From ${Thread.currentThread().name}")
            textView.text = "runBlocking() Execution Has Completed"
        }
        GlobalScope.launch(Dispatchers.IO) {
            delay(2000L)
            Log.d("jobs-.join()", "Running From IO Coroutine")
        }
        runBlocking {
            Log.d("jobs-.join()", "Executing From runBlocking()")
            delay(3000L)
            work.join()
            Log.d("jobs-.join()", "Executing Has Completed")
        }
```
->And Make Sure That You Have Implemented **`work.join()`** In runBlocking() As **.join()** Is A Suspend Function.

-> As You Can See In Both Logcat+Mobile Activity Our Variable Is Executed First And Then Other Coroutine Started Their Respected Work/Job Where Variable Is Included In **runBlocking()**:

![.join() with runblocking.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1625314181418/iI3g2es3q.png)

![join in runblock.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1625314206673/0FT9FBEmy.gif)

**Case2:**

- If `.join()` Is Executed In **`Coroutine`**:
```
val work = GlobalScope.launch(Dispatchers.Default) {
            Log.d("jobs-.join()", "Started Execution From GlobalScope Which Is In A Variable")
        }
        GlobalScope.launch(Dispatchers.Main) {
            delay(2000L)
            Log.d("jobs-.join()", "Running From ${Thread.currentThread().name}")
            textView.text = "Coroutine Execution Has Completed"
        }
        GlobalScope.launch(Dispatchers.IO) {
            Log.d("jobs-.join()", "Running From IO Coroutine")
            work.join()
            delay(2000L)
        }
```
->And Make Sure That You Have Implemented **`work.join()`** In Coroutine As **.join()** Is A Suspend Function.

-> As You Can See In Both Logcat+Mobile Activity Our Variable Is Executed Simultaneously With That Particular Coroutine In Which I Have Implemented `work` Variable And Then Other Coroutine Started Their Respected Work/Job As Well:

![.join() with coroutine.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1625314235327/kiKEMDxp2.png)

![join in coroutines.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1625314251986/pfJLf20Jp.gif)
- And You Can Notice In Both The Cases That `.join()` Function Is Executed First.

->That's All About **`.join()`** Which Can Be Joined In Other Coroutine To Work Simultaneously With That Coroutine || In runBlocking() To Execute That Variable Primarily.

### .cancel():

> As Name Suggests, Our Job||Work Can Be Cancelled.

-> But Why Would Someone Cancel The Work🤔?

- Let's See Those Cases:

->As You Can See That I Have Added A **`repeat(...){...}`** Block Where I Have Added `Log` Statements By 1 Second Delay Interval, By 5 Times.

- That Means Execution Will Be Completed In 5 Seconds.

```
val work = GlobalScope.launch(Dispatchers.Default) {
            repeat(5) {
                Log.d("jobs-.join()", "Started Execution From GlobalScope Which Is In A Variable")
                delay(1000L)
            }
        }
```

**Case1:**

- If `.cancel()` Is Executed In **`runBlocking()`**:

-> As I Already Mentioned `.cancel()` Will Cancel Our Work Immediately, So I am Delaying For 2 Seconds After That Our Work || Job Will Be Cancelled.

> That Mean repeat's Block Code Will Be Executed 2 Times And Then The Whole Work Will Be Cancelled.

```
val work = GlobalScope.launch(Dispatchers.Default) {
            repeat(5) {
                Log.d("jobs-.cancel()", "Started Execution From GlobalScope Which Is In A Variable")
                delay(1000L)
            }
        }
        runBlocking {
            delay(2000L)
            work.cancel()
            Log.d("jobs-.cancel()", "Work Is Cancelled")
        }
```
->And Make Sure That You Have Implemented **`work.cancel()`** In runBlocking() As **.cancel()** Is A Suspend Function.

-> As You Can See In Logcat That Our Variable Is Executed 2 Times And Then It Got Canceled Immediately:

![.cancel with runBlocking().png](https://cdn.hashnode.com/res/hashnode/image/upload/v1625314270651/WDgz_g9eC.png)
**Case2:**

- If `.cancel()` Is Executed In **`Coroutine`**:

-> As I Already Mentioned `.cancel()` Will Cancel Our Work Immediately, So I am Delaying For 2 Seconds After That Our Work || Job Will Be Cancelled.

> That Mean repeat's Block Code Will Be Executed 2 Times And Then The Whole Work Will Be Cancelled.

```
val work = GlobalScope.launch(Dispatchers.Default) {
            repeat(5) {
                Log.d("jobs-.cancel()", "Started Execution From GlobalScope Which Is In A Variable")
                delay(1000L)
            }
        }
        GlobalScope.launch(Dispatchers.Default) {
            delay(2000L)
            work.cancel()
            Log.d("jobs-.cancel()", "Work Is Cancelled")
        }
```
->And Make Sure That You Have Implemented **`work.cancel()`** In Coroutine As **.cancel()** Is A Suspend Function.

-> As You Can See In Logcat That Our Variable Is Executed 2 Times And Then It Got Canceled Immediately:

![.cancel with coroutine.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1625314285654/NdMqcOb0t.png)

> **However Cancelling A Coroutine Is Not Easy Always As I have Demonstrated And Cancelled Above, In Few Cases It May Just Crash The Whole Code As Well If You Didn't Properly Handle It. By The Way Most Of The Time, You May Execute **`withTimeOut(...){...}`** Rather Than Cancelling The Coroutine Which Makes Things More Simple Though.**

->That's All About **`.cancel()`** Which Can Cancel The Execution (If You Want).

### *isActive*:

-> With `.cancel()` We Can Cancel The Execution But The Job || Work Which We Have Executed To A Variable Won't Know That Job || Work Has Cancelled, To Over Come That ***`isActive()`*** Can Be Used With **`if`** Statement As I Have Demonstrated Below:

- In Other Words, You Can Manually Check That Is Our Coroutine Active Or Not:

```
val work = GlobalScope.launch(Dispatchers.Default) {
            if (isActive) {
                repeat(5) {
                    Log.d(
                        "jobs-isActive",
                        "Coroutine Is Still Active"
                    )
                    delay(1000L)
                }
            }
        }
        runBlocking {
            delay(2000L)
            work.cancel()
            Log.d("jobs-isActive","Coroutine Has Cancelled")
        }
```
-> As You Can See That Once Coroutine Knows That Job Is Canceled It Will Leave The Execution.

![isActive Logcat.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1625314313550/WNt18ySwH.png)

-> And If You Actually Notice The Code You'll Notice That If Haven't Added **`work.join`** But The Code Is Executing As I Have Added It, How's That Possible🤔?

- It's Because As I Have Added **`isActive`** In The Code, The Variable Continues To Work Until That Particular Variable Is Cancelled; As It Didn't Get Cancelled That Mean It's Still Active To Execute. That's The Reason Even Though I Didn't Mention `work.join`, Coroutine Will Work Like Charm ⚡. But Once That Variable Gets Cancelled The Variable Will No Longer Be Active.

### *withTimeOut(...){...}*:

- If You Are Thinking That ***"Is It Possible To Set A Particular Time And If That Work || Job Didn't Get Completed In That Particular Time It Should Be Cancelled Automatically"*** Without *runBlocking()*.

-> And The Answer Is:

- **`Yes`** It's Possible With *`withTimeOut(...){...}`* In Which You'll Give A Specific Time If That Job || Work Didn't Get Completed Within That Particular Time It Will Be Cancelled Automatically Without Any *runBlocking()* And Other Stuff:

```
GlobalScope.launch(Dispatchers.Default) {
            withTimeout(2000L) {
                repeat(5) {
                    Log.d("jobs-withTimeOut", "Coroutine Is Still Active")
                    delay(1000L)
                }
            }
        }
```
-> As You Can See That Once The Given Time Completes Coroutine Is Cancelled Automatically:

![withtimeout.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1625314345817/_SVnONe0b.png)

- You Can Even Check That Is Our Coroutine Active In *withTimeOut(...){...}*:
```
GlobalScope.launch(Dispatchers.Default) {
            withTimeout(2000L) {
                if (isActive) {
                    repeat(5) {
                        Log.d("jobs-withTimeOut", "Coroutine Is Still Active")
                        delay(1000L)
                    }
                }
            }
        }
```
-> As You Can See That Once The Given Time Completes And Coroutine Becames InActive, Coroutine Is Cancelled Automatically:

![withtimeout with isactive.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1625314357146/fQW5N_nx2.png)

> `Well That's All For Now🙌`

> **`Bye🤗`**