L-R
L-R

Reputation: 1232

tornado + python. wait for chained operations to finish with callbacks

I have a simple handler like so :

@asynchronous
@gen.engine
def post(self):
    result = functionOne()
    foo = MyObject()
    bar = AnotherObject()

But I'd like for result to come back, then execute foo, then bar in a sequence. I've tried using nested callbacks as per http://www.tornadoweb.org/documentation/gen.html but without much luck.

thx!

Upvotes: 1

Views: 1991

Answers (1)

sean
sean

Reputation: 3985

You are using the gen.engine, but you need to make your method calls into yield with a gen.Task calls.

Example from a project of mine:

customer = yield gen.Task( self.paymentService.charge, email, cardUri )
sub = yield gen.Task( self.paymentService.schedule, customer )

The above will first do the charge part then return the result and then call the schedule and return the subscription object from that call.

The methods that you call with the gen.Task need to look like the following:

def customer( self, email, cardUri, callback=None ):

Where callback will be the function to call to actually return the value from the gen.Task.

I have it setup to work with or without the callback so I can more easily test my methods. Which is just a simple test if callback is None or not and if it isn't None then it gets called else it returns normally.

gen.Engine Docs

EDIT:

Ok, to clarify the above it a complete function that works with gen.Tasks looks like the following.

def delete_customer( customer_id, callback ):
    result = customerService.delete( customer_id )

    callback( result )

The above when used in a gen.Task will look like the following.

@gen.engine
def perform_customer_delete( customer_id )
    result = yield gen.Task( delete_customer, customer_id )
    return result

This means to call the delete_customer with the parameter customer_id and then I want the result of this function, the return, to be given to result. So, when the delete_customer function completes and sends the result of the self.customerService.delete to the callback the gen.Task will then return the value and store it in result.

Now, you can use the result however you wish, in my example it is returned.

For you example it looks like you want to do the following.

@asynchronous
@gen.engine
def post(self):
    result = yield gen.Task( functionOne )
    foo = yield gen.Task( MyObject )
    bar = yield gen.Task( AnotherObject )

This will first call the functionOne then go to the next gen.Task and execute the MyObject and so forth. But, you must follow the pattern where you have a callback parameter in your methods in order to use gen.Task correctly otherwise you cannot get the result back.

This is where my second, confusing example, comes into play.

In order to get around this requirement and to allow my code to work with gen.Task and without I do the following.

def delete_customer( customer_id, callback=None ):
    result = customerService.delete_customer( customer_id )
    if callback:
        callback( result )
    return result

This allows me to use the code in a lot more situations without breakage.

Upvotes: 4

Related Questions