How do you complete tasks without the user having to wait for a response?

After the user has created a new product, in my application, I perform several operations such as updating multiple tables: statistics, finance, usage, stock, etc.

Users should now wait for all steps to complete. If many custom tray are doing this at the same time, the latency is much longer and this is not so good.

My plan is to create a custom TASK_TABLE (product_id, time, task_id) and then run those tasks in the background. BUT:

  • the oldest,
  • do not stop the user for the next action,
  • complete these tasks as quickly as possible.

How can I do this in Symfony?

What's the best way to do this?

+3


source to share


1 answer


What's the best way to do this?

I don't know if this is the "best way", but the most common way to handle this type of case (based on the information provided) is

  • decouple "operations" (update statistics, financial information, usage, promotions, etc.) in one or more services so that you can reuse it anywhere.
  • create a class "event" (at the end it's a simple DTO), in your case could be NewProductEvent

    where the new product object is stored:
  • create a "listener" class NewProductListener

    , where is the handle that performs the "operation", in what order, etc.

Users should now wait for all steps to complete.

To avoid this, we need to be able to "dispatch" our event new_product_created

only after the response has already been sent to the client, and we could do this using a service tag and more specifically by terminating the kernel .

But how to store the product data to get it to kernel.terminate?

Let go of it to implement it.

Class "event":

use Symfony\Component\EventDispatcher\Event;
use YourApp\YourBundle\Entity\Product;

class NewProductEvent extends Event
{
    const EVENT_NAME = 'new_product_created';

    protected $product;

    public function __construct(Product $newProduct)
    {
        $this->product = $newProduct;
    }

    public function getProduct()
    {
        return $this->product;
    }
}

      

Listener class:



class NewProductListener
{
    protected $product;

    public function __construct()
    {
        # then you can inject all dependencies needed to perform your tasks
    }

    public function onNewProductCreated(Product $newProduct)
    {
        # here you keep in memory the product data!
        $this->product = $newProduct->getProduct();
    }

    public function performTasks()
    {
        if ($this->product) {
            # here you can put the logic to perform all needed tasks!
        }
    }
}

      

The definition of a "service" listener:

<service id="new_product_listener"
         class="YourApp\YourBundle\Event\NewProductListener">
    <!-- you can inject in the listener, as argument, each service task you need -->
    <!-- <argument type="service" id="financial_operation_service"/>-->
    <!-- <argument type="service" id="usage_operation_service"/>-->
    <tag name="kernel.event_listener" event="new_product_created" method="onNewProductCreated"/>
    <tag name="kernel.event_listener" event="kernel.terminate" method="performTasks"/>
</service>

      

Now for a practical example (I am not commenting on the code as it explains itself):

// presuming you are in a controller:
$dispatcher = $this->get('event_dispatcher');
$newProduct = //--- I don't know from where it will come.
$event      = new NewProductEvent($newProduct);
$dispatcher->dispatch(NewProductEvent::EVENT_NAME, $event);

      

By dispatching NewProductEvent :: EVENT_NAME ( new_product_created

) you will store the product data that triggers the method onNewProductCreated

in the $product

listener variable NewProductListener

so that you can use it after kernel.terminate

it has been called!

This way Symfony will perform the necessary tasks (in the background) and not degrade the user experience.

Some links:

+4


source







All Articles