Error on async job

前端 未结 4 1363
轻奢々
轻奢々 2021-02-20 16:39

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 i

相关标签:
4条回答
  • 2021-02-20 16:48

    In my case, just returning a promise worked.

    MyService.groovy

    import static grails.async.Promises.*
    def getAsync(){
        Promise p = task {
        //Long running task
                println 'John doe started digging a hole here.'
                Thread.sleep(2000)
                println 'John doe working......'
                return 'Kudos John Doe!'
            }
            p.onError { Throwable err ->
                println "Poor John"+err
            }
            p.onComplete { result ->
                println "Congrats." +result
            }
    
            println 'John Doe is doing something here.'
            return p
    }
    
    0 讨论(0)
  • 2021-02-20 16:57

    When creating a Promise async task inside a controller you actually have to return the response by calling the get() method on the task, or the onError and onComplete methods will never be called. Adding:

    job1.get()
    

    Before your call to render will resolve the issue.

    0 讨论(0)
  • 2021-02-20 17:04

    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 }
    
    0 讨论(0)
  • 2021-02-20 17:05

    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

    0 讨论(0)
提交回复
热议问题