pythonwebsockettornadoasyncsocket

how to correctly use async with tornado


in the documents of tornado is mentioned that gen.coroutine decorators are from older versions. newer should be used with aysnc. So far a I failed to convert that little code for Tornado 6.0.3.

import tornado.web
import tornado.websocket
import tornado.httpserver

from random import randint
from tornado import gen
from tornado.ioloop import IOLoop

from web_handlers import HandlerIndexPage
from web_handlers import HandlerWebSocket

msg = 'none'

@gen.coroutine
def generate_random_int():
    global msg
    while True:
        msg = str(randint(0, 100))
        print('generated:', msg)
        yield gen.sleep(1.0)

@gen.coroutine
def generate_message_to_sockets():
    global msg
    while True:
        print ('new messageToCon: ', msg)
        yield [con.write_message(msg) for con in HandlerWebSocket.connections]
        yield gen.sleep(1.0)


class webApplication(tornado.web.Application):
    def __init__(self):
        handlers = [
            (r'/', HandlerIndexPage),
            (r'/websocket', HandlerWebSocket)
        ]

        settings = {
            'template_path': 'templates'
        }
        tornado.web.Application.__init__(self, handlers, **settings)

if __name__ == '__main__':
    ws_app = webApplication()
    server = tornado.httpserver.HTTPServer(ws_app)
    port = 9090
    server.listen(port)
    print('websocket listening on port:'+ str(port))
    IOLoop.current().spawn_callback(generate_random_int)
    IOLoop.current().spawn_callback(generate_message_to_sockets)
    IOLoop.instance().start()

How would I correctly use async ?


Solution

  • In Tornado, coroutines are generators that can be plugged into Python2 whereas async functions are first-class citizens in Python3. They have different semantics (for instance, you can return from an async function, but not a coroutine). The code you would need to change would look like:

    ...
    
    import asyncio
    
    ...
    
    async def generate_random_int():
        global msg
        while True:
            msg = str(randint(0, 100))
            print('generated:', msg)
            await gen.sleep(1.0)
    
    async def generate_message_to_sockets():
        global msg
        while True:
            print ('new messageToCon: ', msg)
            futures = [con.write_message(msg) for con in HandlerWebSocket.connections]
            if futures:
                await asyncio.wait(futures)
            await gen.sleep(1.0)
    ...
    

    You can see an example similar to your use-case here: https://www.tornadoweb.org/en/stable/ioloop.html#ioloop-objects