48

I have the following code:

async some_callback(args):
    await some_function()

and I need to give it to a Thread as a target:

_thread = threading.Thread(target=some_callback, args=("some text"))
_thread.start()

The error that I get is "some_callback is never awaited".
Any ideas how can I solve this problem?

Damian
  • 1,084
  • 3
  • 14
  • 26

5 Answers5

70

You can do it by adding function between to execute async:

import asyncio

async def some_callback(args):
    await some_function()

def between_callback(args):
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)

    loop.run_until_complete(some_callback(args))
    loop.close()

_thread = threading.Thread(target=between_callback, args=("some text"))
_thread.start()

alper
  • 2,919
  • 9
  • 53
  • 102
norbeq
  • 2,923
  • 1
  • 16
  • 20
66

As of Python 3.7, you can use asyncio.run() which is a bit more straightforward than loop.run_until_complete():

_thread = threading.Thread(target=asyncio.run, args=(some_callback("some text"),))
_thread.start()
mckbrd
  • 859
  • 9
  • 11
  • How does this actually launch something in a thread? It looked strange to me, and then I tested it and the `some_callback` does indeed execute *before* you start the thread, like it looks like it is going to. You know, because you are calling a sync function and all. No? – AntonOfTheWoods Dec 05 '20 at 07:57
  • 5
    @anton Not sure to understand the question. `some_callback("some_text")` returns a coroutine object, see [the doc](https://docs.python.org/3/library/asyncio-task.html#coroutines): "simply calling a coroutine will not schedule it to be executed" and "To actually run a coroutine, asyncio provides three main mechanisms", among which `asyncio.run`. In my answer, `asyncio.run` is the target of the thread and takes the coroutine object returned by the `some_callback` function as argument, it is therefore executed in the thread – mckbrd Feb 25 '21 at 21:11
  • 2
    sure, not sure what my thinking was back then but 3 months ago I was still struggling to come to terms with async (even more than now anyway!) so probably was trying to pass a normal function. Anyway, sorry for the noise! – AntonOfTheWoods Feb 26 '21 at 02:55
  • 2
    This might be problematic because the coroutine is created in a different event loop than the loop in which it is run. Something like [this](https://stackoverflow.com/a/70225945), while more verbose, is probably better. – brianmaissy Dec 19 '21 at 09:26
  • 1
    [ERROR](https://stackoverflow.com/questions/68431205/typeerror-asyncio-runners-run-argument-after-must-be-an-iterable-not-corou) – Milovan Tomašević Sep 28 '22 at 11:21
2

After Python 3.7, you can use asyncio.run()

import asyncio
import threading

async def some_callback(args):
    await some_function()

def wrap_async_func(args):
    asyncio.run(some_callback(args))

_thread = threading.Thread(target=wrap_async_func, args=("some text"))
_thread.start()
eric xu
  • 523
  • 5
  • 13
1

Didn't quite work for me, if you are trying to let it run next to your code (example: discord wait for emote reaction aswell as add emotes), you could try to use:

asyncio.get_event_loop().create_task(FUNKTION(ARGUMENT))
GhostCat
  • 137,827
  • 25
  • 176
  • 248
0

Just wrap your async function with this method and run the thread. _target is name of function in string and **args all your args. in addition to Norbeck solution

def middleware_callback(**args,_target):
_proc = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
_proc.run_until_complete(getattr(sys.modules[__name__], _target)(**args)
_proc.close()
Awais khan
  • 37
  • 6