Asynchronous Python - fire and forget HTTP request

2019-07-27 04:03发布

问题:

Not sure if that's achievable. I want to fire an HTTP POST request from a script, but not wait for a response. Instead I want to return immediately.

I tries something along these lines:

#!/usr/bin/env python3

import asyncio
import aiohttp

async def fire():
    await client.post('http://httpresponder.com/xyz')

async def main():
    asyncio.ensure_future(fire())

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    client = aiohttp.ClientSession(loop=loop)
    loop.run_until_complete(main())

The script returns immediately without errors, but the HTTP request never arrives at the destination. Can I fire the POST request, but not wait for response from the server, just terminate the moment the request is sent?

回答1:

I have answered a rather similar question.

async def main():
    asyncio.ensure_future(fire())

ensure_future schedules coro execution, but does not wait for its completion and run_until_complete does not wait for the completion of all futures.

This should fix it:

async def main():
    await fire()


回答2:

Using asyncio you can write a simple decorator as @background. Now you can write whatever inside foo() and the control-flow will not wait for its completion.

import asyncio
import time


def background(f):
    from functools import wraps
    @wraps(f)
    def wrapped(*args, **kwargs):
        loop = asyncio.get_event_loop()
        if callable(f):
            return loop.run_in_executor(None, f, *args, **kwargs)
        else:
            raise TypeError('Task must be a callable')    
    return wrapped


@background
def foo():
    time.sleep(1)
    print("foo() completed")


print("Hello")
foo()
print("I didn't wait for foo()")

Produces

>>> Hello
>>> I didn't wait for foo()
>>> foo() completed