Inside the JsonImporter::import() method, once we get our hands on the $products array, let's replace the loop with the following:
$batch = [
'title' => $this->t('Importing products'),
'operations' => [
[[$this, 'clearMissing'], [$products]],
[[$this, 'importProducts'], [$products]],
],
'finished' => [$this, 'importProductsFinished'],
];
batch_set($batch);
Creating a batch involves a number of steps, the first one being the creation of a batch definition, which is nothing more than an array with some data. The title option sets the title to be used on the progress page. Similarly, we also have the optional init_message, progress_message and error_message, which have some sensible defaults, so we won't set them here. For more information as to what exactly you can do with them, and what other options you have, make sure you check out the documentation of the batch_set() function.
The most important part of the batch definition is the operations array in which we list the operations that need to take place in the batch. These are defined as any kind of valid PHP callback and an array of arguments to pass to these callbacks. If the latter resides in a file that is has not been loaded, the file options can also be set to specify the file path to include. Each operation runs on its own PHP request; in the sequence, they are defined. Moreover, each operation can also run across multiple requests, similar to how we wrote our update hook earlier.
Our first operation will be responsible for removing from Drupal the products which no longer exist in the JSON response, while the latter will do the import. Both of these receive only one parameter--the array of products.
The finished key is another callback that gets fired at the end of the batch processing, after all the operations are done.
Finally, we call the global batch_set() method, which statically sets the batch definition and marks it as ready to be run. There is just one more step to trigger the batch, and that is a call to batch_process(). But the reason we have not used it is because if the import runs as part of a form submission, the Form API triggers it automatically. So it won't work if we trigger it here as well. The reason why the Form API does it for us is because most of the time we want batches to run only as a result of an action being taken. Usually, this is done via forms. However, the other major possibility is to trigger the batch via a Drush command (which we can actually do). In this case, we need to use the drush_backend_batch_process() function instead.
So, what we will do is first is check that we are in a command-line environment (aka Drush) and trigger it only in that case:
if (PHP_SAPI == 'cli') {
drush_backend_batch_process();
}
Otherwise, we leave it up to the Form API. In doing this, we can trigger the import both from a Form submit handler and via Drush, and we can have plugins that don't necessarily use batches.