Enhancing User Experience with Asynchronous Job Notifications

·

5 min read

Table of contents

No heading

No headings in the article.

In web applications, it's common to have long-running processes that need to be executed asynchronously. When these processes take a long time to complete, it's important to provide feedback to the user so that they know what's happening and can continue to interact with the website while the process is running.

In this blog post, we'll look at how to implement a long-running asynchronous job with notifications in a web application with a React frontend and Spring backend. We'll use the job ID to track the status of the job and periodically poll the backend to check the status of the job. When the job is complete, we'll show a notification to the user indicating that the job is complete.

Prerequisites:

To follow along with this tutorial, you should have a basic understanding of React and Spring. You should also have a web application set up with React frontend and Spring backend.

Step 1: Starting the Job and Returning the Job ID

When the user initiates the long-running job, send a request to the Spring backend to start the job and return a job ID to the frontend. You can use a POST request to send the job data to the backend and receive the job ID in the response.

Here's an example code snippet for starting the job in the Spring backend:

@PostMapping("/jobs")
public ResponseEntity<Long> startJob(@RequestBody JobData jobData) {
    // Start the job and get the job ID
    long jobId = jobService.startJob(jobData);

    // Return the job ID in the response
    return ResponseEntity.ok(jobId);
}

In the frontend, you can use the fetch function to send the request to the backend and receive the job ID in the response.

fetch('/jobs', {
  method: 'POST',
  body: JSON.stringify(jobData),
  headers: {
    'Content-Type': 'application/json'
  }
})
.then(response => response.json())
.then(jobId => {
  // Store the job ID in the state or elsewhere
})
.catch(error => {
  // Handle the error
});

Step 2: Checking the Status of the Job

Create an endpoint in the Spring backend to check the status of the job based on the job ID. This endpoint should return the current status of the job (e.g., "in progress", "completed", "failed") and any relevant data (e.g., job result) when the job is completed.

Here's an example code snippet for checking the status of the job in the Spring backend:

@GetMapping("/jobs/{jobId}/status")
public ResponseEntity<JobStatus> getJobStatus(@PathVariable long jobId) {
    // Get the status of the job based on the job ID
    JobStatus jobStatus = jobService.getJobStatus(jobId);

    // Return the job status in the response
    return ResponseEntity.ok(jobStatus);
}

In the frontend, you can use the setInterval function to make periodic requests to the backend endpoint to check the status of the job.

javascriptCopy codeconst interval = setInterval(() => {
  fetch(`/jobs/${jobId}/status`)
  .then(response => response.json())
  .then(jobStatus => {
    if (jobStatus === 'completed') {
      // Show the notification to the user
      clearInterval(interval);
    }
  })
  .catch(error => {
    // Handle the error
  });
}, 5000);

Step 3: Showing the Notification

When the job status changes to "completed", show a notification to the user indicating that the job is complete and display the job result or a link to view the result.

In the frontend, you can use a notification library like React-Toastify to show the notification to the user. You can also store the job result in the state or elsewhere and display it to the user.

javascriptCopy codeimport { ToastContainer, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';

function App() {
  const [jobResult, setJobResult] = useState(null);

  useEffect(() => {
    const interval = setInterval(() => {
      fetch(`/jobs/${jobId}/status`)
      .then(response => response.json())
      .then(jobStatus => {
        if (jobStatus === 'completed') {
          clearInterval(interval);
          fetch(`/jobs/${jobId}/result`)
          .then(response => response.json())
          .then(result => {
            setJobResult(result);
            toast.success('The job is complete!');
          })
          .catch(error => {
            // Handle the error
          });
        }
      })
      .catch(error => {
        // Handle the error
      });
    }, 5000);
  }, [jobId]);

  return (
    <div>
      {jobResult && (
        <div>
          <h2>Job Result:</h2>
          <pre>{JSON.stringify(jobResult, null, 2)}</pre>
        </div>
      )}
      <ToastContainer />
    </div>
  );
}

In this example, we use the toast.success function from React-Toastify to show a success notification to the user when the job is complete. We also fetch the job result from the backend and store it in the state. We display the job result to the user if it's available.

(please note this code is for idea demonstration)

Following are few websites which uses similar notification mechanism

  1. Asana: Asana uses a notification bell icon on the top right corner of the screen to show the user any updates or notifications related to their tasks or projects. The notification dropdown allows the user to see the details of each notification.

  2. Trello: Trello uses a similar approach to Asana. The user receives notifications related to their boards and cards, and they can see the details by clicking on the notification bell icon.

  3. GitHub: GitHub uses a notification icon on the top right corner of the screen to show the user any updates related to their repositories or pull requests. The user can click on the notification to see the details.

Conclusion:

In this blog post, we looked at how to implement a long-running asynchronous job with notifications in a web application with a React frontend and Spring backend. We used the job ID to track the status of the job and periodically polled the backend to check the status of the job. When the job was complete, we showed a notification to the user indicating that the job was complete. This approach allows the user to initiate a long-running job and receive a notification when it's complete, while still being able to interact with other parts of the website.