Future 对象

Future 是一种特殊的低层级可等待对象(Task是Future的子类), 表示一个异步操作的最终结果
当一个 Future 对象被等待, 这意味着协程将保持等待直到该 Future 对象在其他地方操作完毕

async def main():
    # 获取当前事件循环
    loop = asyncio.get_running_loop()
    # 创建一个任务(Future对象), 默认未绑定任何行为, 则这个任务永远都不会结束
    fut = loop.create_futute()
    # 等到任务最终结果(Future对象), 没有结果则会一直等待下去
    await fut
import asyncio

async def set_after(fut):
    await asyncio.sleep(2)
    fut.set_result('666')

async def main():
    loop = asyncio.get_running_loop()
    fut = loop.create_future()

    # 创建一个任务, 绑定了 set_after 函数, 函数内部在 2s 后, 会给 fut 赋值
    # 手动设置 future任务的最终结果, 那么 fut 就可以结束了
    await loop.create_task(set_after(fut))
    data = await fut
    print(data)

asyncio.run(main())

Future 对象本身无函数进行绑定, 想要让事件循环获取 Future 的结果, 则需要手动设置
Task 对象继承了 Future 对象并进行了扩展, 可以实现在对应绑定的函数执行完成之后, 自动执行 set_result, 从而实现自动结束

concurrent.futures

在 python 的 concurrent.futures 模块中也有一个 Future 对象, 这个对象是基于线程池或进程池实现异步操作时使用的对象

import time
from concurrent.futures import Future
from concurrent.futures.thread import ThreadPoolExecutor

def func(value):
    time.sleep(1)
    print(value)

pool = ThreadPoolExecutor(max_workers=5)

for i in range(1):
    fut = pool.submit(func, i)
    print(fut)

示例: async + requests

(1) 直接使用, 实际表现形式为同步请求

import asyncio
import requests

async def send_req(url):
    print(f'开始请求 {
     url}')
    res = requests.get(url).text
    print(res)
    print(f'请求结束 {
     url}')
    return res

async def main():
    tasks = [
        asyncio.create_task(send_req('http://httpbin.org/ip')),
        asyncio.create_task(send_req('http://httpbin.org/headers')),
    ]

    await asyncio.wait(tasks)

asyncio.run(main())

(2) run_in_executor 异步请求

import asyncio
import requests
import concurrent.futures


def send_req(url):
    print(f'开始请求 {
     url}')
    res = requests.get(url).text
    print(res)
    print(f'请求结束 {
     url}')
    return res


async def main():
    loop = asyncio.get_running_loop()

    # None, 默认采用 ThreadPoolExecutor 线程池
    # 第一步:内部会先调用 ThreadPoolExecutor 的 submit 方法去线程池中申请一个线程去执行 send_req 函数,并返回一个concurrent.futures.Future对象
    # 第二步:调用 asyncio.wrap_future 将 concurrent.futures.Future 对象包装为 asycio.Future 对象
    tasks = [
        loop.run_in_executor(None, send_req, 'http://httpbin.org/ip'),
        loop.run_in_executor(None, send_req, 'http://httpbin.org/headers')
    ]
    
    await asyncio.wait(tasks)


# concurrent.futures 异步
async def main():
    loop = asyncio.get_running_loop()

    with concurrent.futures.ThreadPoolExecutor() as pool:
        tasks = [
            loop.run_in_executor(pool, send_req, 'http://httpbin.org/ip'),
            loop.run_in_executor(pool, send_req, 'http://httpbin.org/user-agent'),
        ] 

        await asyncio.wait(tasks)

asyncio.run(main())

当项目以协程式的异步编程开发时,如果要使用一个第三方模块,而第三方模块不支持协程方式异步编程时,就可以用到这个功能