使Python松弛的机器人异步

时间:2016-10-30 04:51:53

标签: python asynchronous python-asyncio slack-api slack

我一直在尝试在Slack中制作一个机器人,即使它还没有完成处理先前的命令,它仍然保持响应,所以它可以去做一些需要一些时间而不会锁定的东西。它应该返回先完成的任何东西。

我想我已经在那里得到了一部分:它现在不会忽略在早期命令运行之前键入的内容。但它仍然不允许线程超过"彼此 - 一个名为first的命令将首先返回,即使它需要更长的时间才能完成。

import asyncio
from slackclient import SlackClient
import time, datetime as dt

token = "my token"
sc = SlackClient(token)

@asyncio.coroutine
def sayHello(waitPeriod = 5):
    yield from asyncio.sleep(waitPeriod)
    msg = 'Hello! I waited {} seconds.'.format(waitPeriod)
    return msg

@asyncio.coroutine
def listen():
    yield from asyncio.sleep(1)
    x = sc.rtm_connect()
    info = sc.rtm_read()
    if len(info) == 1:
            if r'/hello' in info[0]['text']:
                print(info)
                try:
                    waitPeriod = int(info[0]['text'][6:])
                except:
                    print('Can not read a time period. Using 5 seconds.')
                    waitPeriod = 5
                msg = yield from sayHello(waitPeriod = waitPeriod)
                print(msg)
                chan = info[0]['channel']
                sc.rtm_send_message(chan, msg)      

    asyncio.async(listen())

def main():
    print('here we go')
    loop = asyncio.get_event_loop()
    asyncio.async(listen())
    loop.run_forever()

if __name__ == '__main__':
    main()

当我在Slack聊天窗口中输入/hello 12/hello 2时,僵尸程序现在会响应这两个命令。但是,在完成/hello 2命令之前,它不会处理/hello 12命令。我对asyncio的理解是一项正在进行的工作,所以我很可能犯了一个非常基本的错误。我在previous question中被告知像sc.rtm_read()这样的东西是阻塞函数。这是我问题的根源吗?

非常感谢, 亚历

1 个答案:

答案 0 :(得分:1)

正在发生的事情是您的listen()协程在yield from sayHello()声明处阻止。只有sayHello()完成后listen()才能继续快乐。{1}}关键是yield from语句(或Python 3.5+中的await)是阻塞的。它将两个协同程序链接在一起,并且“父”协程无法完成,直到链接的“子”协程完成。 (但是,不属于同一链接链的'邻近'协程可以在此期间自由进行。)

在这种情况下,释放sayHello()而不阻止listen()的简单方法是使用listen()作为专用侦听协程,并将所有后续操作卸载到自己的Task中而不是包装器,因此不会阻碍listen()响应后续传入的消息。这些方面的东西。

@asyncio.coroutine
def sayHello(waitPeriod, sc, chan):
    yield from asyncio.sleep(waitPeriod)
    msg = 'Hello! I waited {} seconds.'.format(waitPeriod)
    print(msg)
    sc.rtm_send_message(chan, msg)   

@asyncio.coroutine
def listen():
    # connect once only if possible:
    x = sc.rtm_connect()
    # use a While True block instead of repeatedly calling a new Task at the end
    while True:
        yield from asyncio.sleep(0)  # use 0 unless you need to wait a full second?
        #x = sc.rtm_connect() # probably not necessary to reconnect each loop?
        info = sc.rtm_read()
        if len(info) == 1:
                if r'/hello' in info[0]['text']:
                    print(info)
                    try:
                        waitPeriod = int(info[0]['text'][6:])
                    except:
                        print('Can not read a time period. Using 5 seconds.')
                        waitPeriod = 5
                    chan = info[0]['channel']
                    asyncio.async(sayHello(waitPeriod, sc, chan))