# Async Programming: Do Not Waste Time

In a simple words, async’s philosophy is - *while waiting, do something!*

Async, or asynchronous is not as an *asing* things in programming. However, due to no real world use case for utilization of the concept, I tend to ignore it in my codes. Then it has became my concerns in my current working project as it required me to build a vast and robust pipeline that is operated efficiently and relatively faster.

# Why Use

* Time is money
    
* Speed
    
* Optimization
    

# When to Use

1. Use it whenever you have a function that took so much time, yet you want other function to run int the background or while waiting the *slow guy* to complete.
    
2. Use it when you want to increase the efficiency of the code, increase the potential and speed of the operation by not blocking other function(s).
    

# The Burger Analogy

![burger with lettuce and tomatoes](https://images.unsplash.com/photo-1568901346375-23c9450c58cd?fm=jpg&q=60&w=3000&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxzZWFyY2h8Mnx8YnVyZ2VyfGVufDB8fDB8fHww align="left")

Below is the approaches to complete any process or operation, or any task in life. To understand this, imagine a situation when a man need to cook a burger in a restaurant. The man need to completed 10 set of burgers by following the common cooking process.

### Pure Sync

* A man cooking a burger sequentially (single thread)
    

1. Man cook a patty with no timer.
    
2. Man waiting until the patty cooks and cannot do the next step until the patty is cooked. (**blocking**)
    
3. Once cooked, man slice the bread.
    
4. Man then frying the fries with no timer features. Man waiting until fries is completely cooked. (**blocking**)
    
5. Man put lettuce and mayo on the bread and stack up with the patty.
    
6. Man put fries as side dish.
    
7. Man do step 1 to 4, until it completed 10 burgers.
    
8. Man tired.
    

*Took so so much time. :(*

### Pure Async

* A man cooking a burger (single thread)
    

1. Man cook a patty.
    
2. While waiting patty to cook, man slice a bread.
    
3. Man go flip the patty and set a timer for five minutes.
    
4. While waiting patty cook, man fry fries in a fryer with timer for 3 minutes.
    
5. Man put lettuce on the bread.
    
6. While waiting the patty cook, fry timer ended. Man take the french fries and put in the plate.
    
7. Patty cooked, man put patty on the stacked bread with lettuce.
    

*Fast and efficient.*

### Pure Async + Multithreading

* 4 men cooking a burger (4 threads)
    

1. Man-A cook a patty, Man-B slice bread, Man-C frying fries, Man-D arrange the lettuce.
    
2. While patty cook, Man-B take lettuce on bread, Man-C put the fries on the plate.
    
3. Patty cook, Man B took patty and stack on bread.
    
4. Complete.
    

*Faster, but more complex to control more men.*

---

# Coding Mindset

1. Do async function for function that potentially taking too much time.
    
2. Wrap the expression(s) in a function. Atomize it.
    
3. Prefix with `async` and `await` the async-ing function.
    

# Async Code

In Python, we will handle the async function with `asyncio` Package and it will do all the magics behind it.

## Basics

### Terms

* **coroutine** - when it is async, the function will be call as the coroutine object or function
    

### Task

Task is fancy terms for the function you want to execute. Task is optional. You can explicitly create it with with `asyncio.create_task()` or, directly run it with `asyncio.run()` or `asyncio.gather()` + `asyncio.run()` for multiple async function.

* `asyncio.create_task()` — create task immediately, can run later with await if you want, independent task, cancellable
    
* `asyncio.gather()` — gather all coroutine function together, don’t want run the function later, non cancellable, return list
    

### Non-dependent async function

* simple coroutine functions with [asyncio.run](http://asyncio.run)() and asyncio.gather()
    

```python
async function():
    asyncio.sleep(10) # just to simulate a time consuming function
    return "a"
async function()_1:
    asyncio.sleep(10)
    return "b"
async function()_2:
    asyncio.sleep(10)
    return "c"
async function()_3:
    asyncio.sleep(10)
    return "b"


async def main():
    # singlefunction
    asyncio.run(function()) 

    # multiple async function
    asynction.gather(function_1(), function_2(), function_3())

# run the corouting function
asyncio.run(main())
```

### Dependent function

* function\_a dependent on function\_b to complete first. Hence the async is contagious.
    

```python
import asyncio
async def do_things():
    print("Currently doing things...")
    asyncio.sleep(100)  # fake a function that took so much time!
    return "yes"
    
async def process_output(out):
    if out == "yes":
        return "completed"
    
def check_something():
    out = await do_things()
    process = await process_output(out) # need to await coz do_things took time to complete
    return process
    
asyncio.run(check_somoething)
# await check_something() # notebook
Output:
"yes"
```

### Off-loading blocking tasks

* There is sync function that will blocked the operation (legacy, or unsupported)
    
    [`asyncio.to`](http://asyncio.to)`_thread()` | [`asyncio.loop.run`](http://asyncio.loop.run)`_in_executor()`
    

```python
function():return "a" # sync, legacy or unsupported for async
async function()_1:return "b"
async function()_2:return "c"
async function()_3:return "b"


async def main():
    # singlefunction
    asyncio.run(asyncio.to_thread(function())) # or loop.run_in_executor()

    # multiple async function
    asynction.gather(function_1(), function_2(), function_3())

# run the corouting function
asyncio.run(main())
```

# Potential Bugs

| Issue | Potential Solution |
| --- | --- |
| `function` or expression is not awaited | check whether `function` has `async` or notwrap the expression as `async function` so that it can be awaited, although it does not consume time to return output |
| object `coroutine` is not iterable (list), hashable (dict) | check whether the function call which output is passed to variable is awaited or not |
| `async def load_something(): return "complete" output = load_something() ❌ output = await load_something() ✅` [`asyncio.run`](http://asyncio.run)`(load_somoething) ✅` |  |

# If pure async approach is possible, why there is blocking function?

1. Legacy sync function.
    
2. Unsupported async feature in package.
    
3. Some function easily handled as sync.
    

# Overwhelming. How to start?

1. For time consuming function, make it async. Use `async` in front of `def`.
    
2. Calling async function require you to use `await`. Hence include it if it is called.
    
3. Start with `asyncio.run()` for easy execution of async function. Then can try `asyncio.gather([list of async function])` + `asyncio.run()` for multiple async functions.
    
4. Once comfortable, implement intermediate async feature, such async *time* base, *prioritization* base, and *queue*.
    

# Conclusion

I hope this will help the beginners or any level of programmer to understand the async concept. If you have any commenta, please leave the comment below. I appreciate it. Thank you.
