2014-11-20 12 views
11

Sto cercando di imparare le coroutine del tornado, ma ho un errore nell'usare il codice sottostante.Coroutine tornado

Traceback (most recent call last): 
    File "D:\projekty\tornado\env\lib\site-packages\tornado\web.py", line 1334, in _execute 
    result = yield result 
    File "D:\projekty\tornado\env\lib\site-packages\tornado\gen.py", line 628, in run 
    value = future.result() 
    File "D:\projekty\tornado\env\lib\site-packages\tornado\concurrent.py", line 109, in result 
    raise_exc_info(self._exc_info) 
    File "D:\projekty\tornado\env\lib\site-packages\tornado\gen.py", line 631, in run 
    yielded = self.gen.throw(*sys.exc_info()) 
    File "index.py", line 20, in get 
    x = yield 'test' 
    File "D:\projekty\tornado\env\lib\site-packages\tornado\gen.py", line 628, in run 
    value = future.result() 
    File "D:\projekty\tornado\env\lib\site-packages\tornado\concurrent.py", line 111, in result 
    raise self._exception 
BadYieldError: yielded unknown object 'test' 

Codice:

from tornado.ioloop import IOLoop 
from tornado.web import RequestHandler, Application, url 
from tornado import gen 

class HelloHandler(RequestHandler): 
    @gen.coroutine 
    def get(self): 
     x = yield 'test' 
     self.render('hello.html') 


def make_app(): 
    return Application(
     [url(r"/", HelloHandler)], 
     debug = True 
    ) 

def main(): 
    app = make_app() 
    app.listen(8888) 
    IOLoop.instance().start() 

main() 
+0

Cosa stai cercando di fare alla riga 'x = yield 'test''? –

+0

che è solo un esempio, ho provato a cedere il risultato di una funzione anche - il risultato è lo stesso, questa linea è solo per testare le coroutine – klis87

risposta

26

Come sottolineato da Lutz Horn, il decoratore tornado.coroutine richiede che vengano forniti solo oggetti Future o determinati contenitori contenenti oggetti Future. Quindi provare a produrre uno str causerà un errore. Penso che il pezzo che ti manca sia che qualsiasi posto all'interno di una coroutine in cui si desidera chiamare yield something(), something deve essere anche una coroutine o restituire un Future. Ad esempio, è possibile risolvere il tuo esempio come questo:

from tornado.gen import Return 

class HelloHandler(RequestHandler): 
    @gen.coroutine 
    def get(self): 
     x = yield self.do_test() 
     self.render('hello.html') 

    @gen.coroutine 
    def do_test(self): 
     raise Return('test') 
     # return 'test' # Python 3.3+ 

O anche questo (anche se in genere non si dovrebbe fare in questo modo):

class HelloHandler(RequestHandler): 
    @gen.coroutine 
    def get(self): 
     x = yield self.do_test() 
     self.render('hello.html') 

    def do_test(self): 
     fut = Future() 
     fut.set_result("test") 
     return fut 

Naturalmente, questi sono esempi inventati; dal momento che in realtà non stiamo facendo nulla di asincrono in do_test, non c'è motivo di renderlo una coroutine. Normalmente faresti un qualche tipo di I/O asincrono. Ad esempio:

class HelloHandler(RequestHandler): 
    @gen.coroutine 
    def get(self): 
     x = yield self.do_test() 
     self.render('hello.html') 

    @gen.coroutine 
    def do_test(self): 
     http_client = AsyncHTTPClient() 
     out = yield http_client.fetch("someurl.com") # fetch is a coroutine 
     raise Return(out.body) 
     # return out.body # Python 3.3+ 
+0

Grazie per l'ottima risposta, ho aggiunto time.sleep all'inizio della funzione do_test, e tutto funziona come in codice sincrono, ottengo self.render eseguito dopo che sleep (x) è finito, ho pensato che forse questa funzione viene eseguita in background, non è il caso però ... Qual è il vantaggio di usare le coroutine allora? Forse un altro utente potrebbe aprire la connessione nello stesso tempo prima che il sonno dell'utente precedente sia finito? – klis87

+2

@ user3575996 L'uso di 'yield function()' farà in modo che l'esecuzione della coroutine attenda fino a quando 'function()' è completata. Tuttavia, * non * bloccherà il loop I/O del tornado, quindi se altre richieste da parte dei client arrivano, quelle potrebbero essere gestite nel frattempo. Tuttavia, non è possibile utilizzare 'time.sleep()' per verificarlo, perché è una funzione di blocco. Devi usare una versione non bloccante del sonno. Vedi [questa risposta] (http://stackoverflow.com/a/23876402/2073595) per un esempio. – dano

+0

Grazie ora finalmente ho capito tutto :) – klis87

3

From the documentation:

maggior parte delle funzioni asincrone in Tornado restituiscono un Future; cedere questo oggetto restituisce il suo risultato.

È inoltre possibile yield a list or dict of Futures, che verrà avviato contemporaneamente e eseguito in parallelo; un elenco o dict dei risultati saranno restituiti quando sono tutti rifiniti:

La stringa "test" non è un Future. Prova a cederne uno.

+2

Diciamo che voglio fornire il risultato della query del database, diciamo che la funzione get_all_users() fa questo per me, potresti per favore darmi un esempio dato che esempi di tornado usano alcune classi built-in come AsyncHTTPClient(), come implementare il tuo – klis87