0

I need to save the request into the database after that i have to call an API to sync the data to the other server.

i do the API call using the finally but it seems PHP still processing it, even when i am sending the response in the try clause.

how do i make this asynchronous ? i want to send the response as fast as possible but still processing the API call after the response.

so this what the simple code look like, describing what i am currently doing.

Code with finally =>

 public function store(Request $request)
{
    try {
        //returning the code early
        return response("i am speed", 202);
    } catch (\Throwable $th) {
        return response($th->getMessage(), 500);
    } finally {
        //lets says this is the super long too run
        $i = 0;
        $last = 11111111;
        while ($i <= $last) {
            $i++;
        }
    }
}
//this code finish in 1000ms

code without finally =>

  public function store(Request $request)
{
    try {
        return response("i am speed", 202);
    } catch (\Throwable $th) {
        return response($th->getMessage(), 500);
    }
} //this code finish in 90ms;

why this is happen ?

i already sending the response but why it not returning early ?

what can i do to send the response first then continue the execution ?

ucup
  • 665
  • 6
  • 17
  • 3
    Use a Queue: https://laravel.com/docs/8.x/queues. `dispatch()` the long-running code logic to a Queue and return a response immediately. – Tim Lewis May 06 '21 at 16:33
  • 2
    Laravel doesn't send the response as soon as you return it from the controller so will still wait for the entire controller function to finish. You should use [terminable middleware](https://laravel.com/docs/8.x/middleware#terminable-middleware) if you don't want to set up a queue worker – apokryfos May 06 '21 at 16:37
  • 3
    A finally block may also be specified after or instead of catch blocks. Code within the finally block will always be executed after the try and catch blocks, regardless of whether an exception has been thrown, and before normal execution resumes. https://www.php.net/manual/en/language.exceptions.php#language.exceptions.finally – Jason K May 06 '21 at 16:39
  • 1
    Take a look at this: https://stackoverflow.com/questions/138374/how-do-i-close-a-connection-early – Danial Dezfouli May 06 '21 at 16:46
  • It's possible with Queue as Tim mentioned, But you can do something in pure PHP which for some reasons (IDK why) seems not possible with tiny strings – Danial Dezfouli May 06 '21 at 16:50
  • ok i'll read all of them and decide what to use – ucup May 06 '21 at 16:55

2 Answers2

2

I already finish this problem. As the comment suggest Laravel have a feature called a queue that will dispatch the job the database, this process won't running until you run the queue worker.

To make queue what i do is :

First change the .env QUEUE_CONNECTION to database.

QUEUE_CONNECTION=database

Then run this artisan command to setup the queue worker.

php artisan queue:table
php artisan migrate

After that make a job for the function that want to run in the queue. For example i am gonna put the finally code (the one in the question) in the new job.

php artisan make:job exampleJobName

Then go to exampleJobName.php, write the code that will be running in the queue in the handle function.

  public function handle()
{
   //lets says this is the super long too run code
    $i = 0;
    $last = $this->data; //variable from constructor
    while ($i <= $last) {
        $i++;
    }
} 
//exampleJobName.php

If a variable need to be pass to the handle, then add a contructor in the __construct function.

public function __construct($data)
{
    $this->data = $data;
} 
//still inside the exampleJobName.php

Then everything is setup go to the controller that want to run this job (i am gonna take example from question) and change the code to this.

public function store(Request $request)
{
    try {
        $data = 111111111;
        exampleJobName::dispatch($data);
        //this will insert the job on the jobs table in the database
        //therefore the job won't run until the queue worker is running
       
        return response("i am speed", 202);
    } catch (\Throwable $th) {
        return response($th->getMessage(), 500);
    }
} 
//this code finish fast

Everything is ready then just run the queue worker in the terminal side by side with artisan serve.

php artisan queue:work

The queue worker will check to the jobs table if there are any job that hasn't run yet. then will run it one by one.

That's what i do, hope this can help someone.

credit to Tim Lewis in the comment for show me this this link.

ucup
  • 665
  • 6
  • 17
0

Call an API to sync the data to the other server potentially can take a long time so I suggest that you create and dispatch a job for that.

If you still want to do so right after the response is sent, you might want to use dispatchAfterResponse.

Kevin Bui
  • 2,754
  • 1
  • 14
  • 15