Laravel - Running Jobs in Sequence

Stefan Bogdanescu

Founder & Senior Architect · 2026-06-29

Laravel Company
# Laravel - Running Jobs in Sequence: Achieving Guaranteed Order As developers working with Laravel, especially when leveraging powerful queue systems like Horizon, we often deal with the complexity of asynchronous processing. The beauty of queues is their ability to handle high volumes of tasks concurrently, which significantly improves application responsiveness and scalability. However, as you've encountered, this concurrency introduces a challenge: ensuring that specific operations—like sending emails or updating records in a strict order—are executed one after the other. The scenario you described—dispatching jobs that run across multiple workers rather than sequentially—is perfectly normal when using default queue configurations. To solve this, we need to move beyond simple job dispatching and implement a mechanism for **job chaining**. ## Understanding the Concurrency Problem When you use `SendMailJob::dispatch($subscription)` inside your controller method, Laravel places that job onto the queue. Your Horizon workers (or any queue worker) pull these jobs off the queue as fast as they become available. If you have ten subscriptions, ten workers can simultaneously pick up and process those ten jobs at the same time, leading to non-deterministic execution order. To enforce a strict sequence—where Job B *must* start only after Job A has successfully completed—we need to explicitly link them in a dependency chain rather than treating them as independent tasks. ## The Solution: Implementing Job Chaining The most idiomatic and robust way to ensure sequential execution in Laravel is by chaining jobs together using the `then()` method. This mechanism tells the queue system that the next job should only be dispatched once the preceding job has successfully finished processing. ### Step-by-Step Implementation Instead of dispatching jobs individually, we will design our workflow so that one job triggers the next. This is often best handled by having the first job handle the data retrieval and then chain the subsequent actions. Here is how you can refactor your process to ensure sequential execution: #### 1. Refactor the Job Let's assume you have a primary job that handles the initial task, and subsequent jobs are linked from within it. ```php // app/Jobs/SendMailJob.php use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; class SendMailJob implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; protected $subscription; public function __construct($subscription) { $this->subscription = $subscription; } /** * Execute the job. */ public function handle() { // Step 1: Perform the initial action (e.g., sending an email) \Log::info("Starting mail process for subscription ID: {$this->subscription->id}"); // Step 2: Crucially, chain the next job using then() $this->chainToNextJob(); } /** * Method to chain the next job in the sequence. */ protected function chainToNextJob() { // Dispatch the next job only after this one completes successfully. SendNextSubscriptionJob::dispatch($this->subscription) ->onQueue('emails'); // Optional: Use a specific queue for ordering if needed } } ``` #### 2. Create the Subsequent Job We now create the job that should run immediately after the first one finishes. ```php // app/Jobs/SendNextSubscriptionJob.php use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; // ... other imports class SendNextSubscriptionJob implements ShouldQueue { // ... properties and constructor public function handle() { // Step 3: Perform the next action \Log::info("Successfully completed mail process for subscription ID: {$this->subscription->id}. Proceeding to next step."); // If there were more steps, you would chain again here. } } ``` ### Why This Works (And Laravel Context) By using `dispatch()->then(...)`, you are leveraging the underlying queue infrastructure's promise mechanism. The system is instructed that the execution of `SendNextSubscriptionJob` is dependent on the successful completion of `SendMailJob`. This effectively forces sequential processing within the context of the queue workers, solving your ordering problem while still benefiting from Laravel’s powerful queuing features (as discussed in resources like those found at [https://laravelcompany.com](https://laravelcompany.com)). ## Conclusion For scenarios where strict ordering is paramount—such as financial transactions, sequential state changes, or multi-step workflows—avoid relying on parallel dispatching. Instead, embrace job chaining using the `then()` method. This approach transforms independent tasks into a cohesive sequence, ensuring reliability and predictability in your background processing. By mastering these patterns, you move from simply queuing tasks to orchestrating complex, reliable business processes within your Laravel application.