I have an Event with a bunch of queued listeners. I Can't run sync
because I am calling external APIs etc
Events\Invoice\InvoiceEvent::class => [
Listeners\Invoice\Listener1::class, // should queue
Listeners\Invoice\Listener2::class, // should queue
Listeners\Invoice\Listener3::class, // Should NOT queue......
Listeners\Invoice\Listener4::class, // should queue
Listeners\Invoice\Listener5::class, // should queue
],
Calling this event from a controller method.
public function store(Request $request)
{
$invoice = Invoice::findOrFail($request->id);
InvoiceEvent::dispatch($invoice); // Async event, it cannot be sync
return $invoice; // need to return only when Listener3 finish execution
}
return $invoice
is dependent on Listener3, otherwise, it will return incomplete data.
How can I return only when Listener3 is finished executing?
I came up with sleep(10);
but it's not an ideal solution.
Listener3
saves data from third-party API to the invoices table which needs to be returned, that's why cannot return incomplete invoice data, right now the required data gets added to the invoice but after its return
CodePudding user response:
PHP is natively synchronous. Unless you're pushing those events or listeners to a queue, (i.e. class Listener3 implements ShouldQueue
) then they should run in order. However, you might want to rethink the structure of your code.
Listeners are best as reactions to an event, i.e. side effects, running independently from the rest of your application. Jobs, Events and Listeners should not generally return a value (except to halt
a series of listeners). In your case, the Invoice is going through multiple steps, including calling a 3rd party API. Ideas:
- Create a service class that performs the tasks on the Invoice and returns the invoice to the controller when completed (which then will return the
$invoice
data to the front end) - If you want the process to be async, consider using a push notification. Dispatch a job which performs the tasks on the Invoice, then alert the front end (e.g. Pusher ) when the invoice is ready to fetch.
CodePudding user response:
There are a way to broadcast your event not queuing it,
into your event class add:
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
so your class declaration must implements ShouldBroadcastNow
class NotQueueEvent implements ShouldBroadcastNow { ... }
this spread event without enqueue.
CodePudding user response:
If you want to wait to this method return, you should only don't put it on the queue. Run the event and await the return. Don't know if I understood correctly the problem.