Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error on async job

Tags:

grails

groovy

I'm trying to create an async task that will not block the request. The user make the request, the task will start, and the controller will render "Job is running...", this is for avoid the request being blocked waiting the task to complete. Once the task is finish, it will execute the onComplete and do something with the result of that task (for example call a service that will send a mail to an user)

| Error 2014-09-16 17:38:56,721 [Actor Thread 3] ERROR gpars.LoggingPoolFactory  - Async execution error: null

The code is the following:

package testasync

import static grails.async.Promises.*

class TestController {

  def index() {
    //Create the job
    def job1 = task {
        println 'Waiting 10 seconds'
        Thread.sleep(10000)
        return 'Im done'
    }
    //On error
    job1.onError { Throwable err ->
        println "An error occured ${err.message}"
    }
    //On success
    job1.onComplete { result ->
        println "Promise returned $result"
    }
    render 'Job is running...'
  }

Complete stacktrace:

| Error 2014-09-17 10:35:24,522 [Actor Thread 3] ERROR gpars.LoggingPoolFactory  -  Async execution error: null
Message: null
   Line | Method
 ->>   72 | doCall    in org.grails.async.factory.gpars.GparsPromise$_onError_closure2
  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
  |     62 | run       in groovyx.gpars.dataflow.DataCallback$1
  |   1145 | runWorker in java.util.concurrent.ThreadPoolExecutor
  |    615 | run       in java.util.concurrent.ThreadPoolExecutor$Worker
  ^    745 | run . . . in java.lang.Thread
like image 262
agusluc Avatar asked Sep 16 '14 20:09

agusluc


People also ask

How do you catch error in async function?

A Try Catch with a Rejecting Promise Remember that an async function always returns a promise: When no return statement defined, or a return statement without a value, it returns a resolving promise, equivalent to return Promise. Resolve() .

How do I return async function error?

Async functions and async methods do not throw errors in the strict sense. Async functions and async methods always return a Promise, either resolved or rejected. You must attach then() and catch() , no matter what. (Or wrap the method inside try/catch ).

How do you handle error in await?

Error handlingIf a promise resolves normally, then await promise returns the result. But in the case of a rejection, it throws the error, just as if there were a throw statement at that line. In real situations, the promise may take some time before it rejects.

Which is the best standard approach on error handling for async function?

Error handlingA try/catch block can be used to handle asynchronous errors in an async function. Alluding to the fact that an async function always return a Promise, one can opt to use a . catch() in place of a whole try/catch block.


2 Answers

I ended using the executor framework with the grails-executor plugin. I uploaded a very basic example here: https://github.com/agusl88/grails-async-job-queuqe

That code is using a "custom" version of the grails-executor plugin, i merged some PR's from the plugin repo and packaged as jar just for testing propuses. The repo of the plugin is this: https://github.com/basejump/grails-executor

like image 169
agusluc Avatar answered Sep 17 '22 15:09

agusluc


I was able to get rid of this exception in a controller by removing the onComplete and onError calls. I guess the exception happens because the parent thread ended when you called render.

So your:

Promise p = task {
    complexAsyncMethodCall(); // (1) do stuff
}
.onComplete { result -> println result } // (2) on success
.onError { Throwable t -> System.err.println("Error: " + t) } // (3) on error

Becomes:

Promise p = task {
    try {
        def result = complexAsyncMethodCall(); // (1) do stuff
        println result // (2) on success
    } catch(Throwable t) {
        System.err.println("Error: " + t) // (3) on error
    }
}

This adds coupling between your work (1) and the result processing (2 and 3) but you could overcome this by writing your own Closure wrapper that takes extra Closures as arguments. Something like this:

// may not work! written off the top of my head
class ProcessableClosure<V> extends Closure<V> {
    Closure<V> work;
    Closure<?> onError;
    Closure<?> onComplete;

    @Override
    public V call(Object... args) {
        try {
            def result = work.call(args); // (1) do stuff
            onComplete.call(result); // (2) on complete
        } catch(Exception e) {
            onError.call(result); // (3) on error
        }
    }
}

That makes your code more readable:

Closure doWork = { complexAsyncMethodCall(); } // (1) do stuff
Closure printResult = { println it } // (2) on complete
Closure logError = { Throwable t -> log.error t } // (3) on error
Closure runEverythingNicely = new ProcessableClosure(work: doWork, onComplete: printResult, onError: logError)
Promise p = task { runEverythingNicely }
like image 36
pyb Avatar answered Sep 20 '22 15:09

pyb