---
# System prepended metadata

title: Async Await
tags: [python]

---

# Async Await
###### tags: `python`
Python從3.5之後就開始支援async await語法
建議使用3.7以上
3.8之後的版本會相對較穩定

底層是使用 yield 以及 yield from，詳細可以看[這篇](https://chriskang028.medium.com/python-%E8%A3%A1%E7%9A%84-yield-%E8%AE%93%E4%BD%A0%E7%B0%A1%E5%96%AE-%E5%BF%AB%E9%80%9F%E7%9E%AD%E8%A7%A3-yield-%E7%9A%84%E6%A6%82%E5%BF%B5-f660521f3aa7)。

最主要的lib為
```python=
import asyncio
```
大部分併發所需要的東西都在裡面

先舉個例子

```python=
async def main():
     await asyncio.sleep(1)
```
這樣是一個簡單的併發函式
Type 是 Coroutine
但是Coroutine是沒有辦法直接 call 的
需要搭配 event loop才能做一個循環
所以如果要運行併發可以使用 `asyncio.run()`

```python=
asyncio.run(main())
# 這個函式已經把loop所需的東西都包含進去了
```

另外如果想要用較低階的方式

```python=
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
```

以上的情況僅限於在 ***Main Thread*** 中get_event_loop
額外開的 Thread 是沒有 event loop的
所以如果想在其他 Thread 中執行event loop


```python=
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(main())
```

:::danger
如果在某段開始使用了 event loop 觸發了併發，那麼之後的 function 都要有async await，不然會阻塞。
:::


實際操作併發範例

```python=
async def A():
    await B()
    
async def B():
    await C()
    
async def C():
    await asyncio.sleep(1)
    
asyncio.run(A())
```


一般來說不會只觸發一個task
大多都是好幾個task一起進去event loop

例如

```python=

import asyncio

import time

now = lambda: time.time()

async def dosomething(num):
    print(f'第 {num} 個任務')
    await asyncio.sleep(2)
    
async def main():
    tasks = [dosomething(num) for num in range(1, 10)]
    await asyncio.gather(*tasks)
    
if __name__ == "__main__":
    start = now()
    asyncio.run(main())
    print('Total spend time: ' , now() - start)
    
# ---------result----------
# 第 1 個任務
# 第 2 個任務
# 第 3 個任務
# 第 4 個任務
# 第 5 個任務
# 第 6 個任務
# 第 7 個任務
# 第 8 個任務
# 第 9 個任務
# Total spend time:  2.006014108657837
```
由上面看到，如果使用併發，sleep的時間是共用的
簡單來說就是做了第一件事情到 await 這個語法時會先觸發下一件事情，避免時間的浪費

`asyncio.gather()` 是可以把整個 List 內的 task 封裝成一個大的 Coroutine
也可以傳入參數裡面看最終的值

```python=
result = await asyncio.gather(*task)
print(result)
# ----result----
# ['第 1 個任務', '第 2 個任務'....]
```
一般來說大致上會用到的是這些

[參考資料](https://www.maxlist.xyz/2020/03/29/python-coroutine/)



[2017 Pycon async介紹](https://www.youtube.com/watch?v=iG6fr81xHKA)



https://stackoverflow.com/questions/27796294/when-using-asyncio-how-do-you-allow-all-running-tasks-to-finish-before-shutting
^ ^ ^
這個是比較奇杷的用法，如果你不知道會用多少task完成，就在event loop裡面create task且gather起來，就可以無限創造併發
(如果有設定斷點還是會把剩下的task做完)
