How can I use Await in a non async function?
Asked Answered
D

3

14

I'm working on a discord bot and trying to solve this. I want to know how could I use await in non async function or if I can await a function in the lambda line the last line in the code.

I have tried doing asyncio.run with the player variable and I have tried asyncio.run_coroutine_threadsafe too and it didnt work.

Any ideas on how to make it work?

Code:

def queue_check(self, ctx):
    global queue_list
    global loop
    global playing


    if loop is True:
        queue_list.append(playing)
    elif loop is False:
        playing = ''

    if ctx.channel.last_message.content == '$skip' or '$s':
        return

    song = queue_list.pop(0)
    player = await YTDLSource.from_url(song, loop=self.client.loop, stream=True)

    
    ctx.voice_client.play(player, after= queue_check(self,ctx))





    @commands.command(aliases=['p'])
    @commands.guild_only()
    async def play(self, ctx, *, url):
        global queue_list
        global playing
            
        if ctx.voice_client is None:
            if ctx.author.voice:
                await ctx.author.voice.channel.connect()
            else:
                return await ctx.send("> You are not connected to a voice channel.")



        async with ctx.typing():
                
            player = await YTDLSource.from_url(url, loop=self.client.loop, stream=True)
            await ctx.send(f'> :musical_note: Now playing: **{player.title}** ')
            
            playing = url


            await ctx.voice_client.play(player, after=lambda e: queue_check(self,ctx))
Disinterest answered 5/3, 2021 at 11:51 Comment(1)
You can't. Any function that uses await must be defined as asyncHigley
F
2

Need to import asyncio

To actually run a coroutine, asyncio provides three main mechanisms:

  • The asyncio.run() function to run the top-level entry point “main()” function (see the above example.)
  • Awaiting on a coroutine. The following snippet of code will print “hello” after waiting for 1 second, and then print “world” after waiting for another 2 seconds:

Example

import asyncio
import time

async def say_after(delay, what):
    await asyncio.sleep(delay)
    print(what)

async def main():
    print(f"started at {time.strftime('%X')}")

    await say_after(1, 'hello')
    await say_after(2, 'world')

    print(f"finished at {time.strftime('%X')}")

asyncio.run(main())

OutPut

started at 17:14:32
hello
world
finished at 17:14:34

Details here

Facture answered 5/3, 2021 at 12:23 Comment(0)
K
2

I am also very new to asynchronous programming and took a pretty deep dive into it today. So it is a pretty firm rule that you cannot use await in a non-async function.

so you cannot do:

def foo(delay):
    await asyncio.sleep(delay)

async must be paired with await. However, you can add tasks to the event loop. I just wanted to send a message so I did something like:

def queue_check(self, ctx):
    #some preceding code
    #
    #adding a function to the event loop
    self.bot.loop.create_task(ctx.send("my message"))

Whenever asyncio decides it has enough resources it sends that message. However, in your case. since you need player right then and there this might not be suitable.

If I can suggest a alternative approach, instead of putting strings in the queue:

song = queue_list.pop(0) #im confident song is a string

why don't you enqueue YTDLSource objects instead?

async def Q(self, ctx, url):
    """Add songs to Queue"""
    player = await YTDLSource.from_url(url)
    queue_list.append(player)
Krissykrista answered 3/6, 2022 at 23:42 Comment(0)
A
0

If you really need to (although not recommended) you can run an async method from a sync one, agnostic to the fact of an actual loop running:

try:
    asyncio.get_running_loop().run_until_complete(async_method())
except RuntimeError:
    asyncio.run(async_method())
Aesthesia answered 13/7, 2023 at 15:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.