A few weeks ago, I had to implement some code that would need to retry an operation with different parameters if it failed on the first run but only if it did with a specific exception. I thought about it for a few minutes and before I tried to implement it myself, I decided to search Laravel's documentation to see if there was a helper or something that I could use. Turns out there is a helper called: retry. Yes, it was exactly what I was looking for and more.

According to the docs, retry accepts the number of attempts, the callback where your implementation goes and a third (optional) parameter that can either be a number of milliseconds or a closure to change the time it will wait before the next attempt. It also accepts a fourth (also optional) parameter: a closure that returns the condition in which the method should retry. For example, if you want to retry only if a specific exception was thrown.

If the callback you implement throws an exception, the retry method will catch that and if it still has more attempts, it will retry, otherwise it will throw that exception. On the other hand, if your callback is successful, the retry method will return the value your callback returned. Very straightforward, huh?

Ok, now that you have an overview of how it works, let's see some code. As an example, let's create a function that gives you three chances of guessing a random number:

function guessTheNumber($num) {
	$value = rand(0, 10);
	if ($value === $num) {
	    return true;
    }
    throw new \Exception('You guessed the wrong number!');
}

function tryYourLuck() {
	return retry(3, function() {
    	return guessTheNumber(5);
    }, 100);
}

The first function (guessTheNumber) accepts a value and compares it to a random one. If they are equal, return true, otherwise throw an exception. It is just a silly example to show how retry works. The second function (tryYourLuck) is where we call guessTheNumber with our guessed value (5) and since we have three chances, we call retry with 3 as the first parameter. Just to show you how it works, I'm also passing a third parameter to retry which is the amount of milliseconds the function should wait before retrying. Now, if we call tryYourLuck and it guessTheNumber returns true in any of the three attempts, tryYourLuck will also return true. But, if it retries three times and all of them threw an exception, then tryYourLuck will throw the last exception.

The feature that really helped me was the option to pass a closure as the fourth argument and use that to decide if we the method should retry. The closure accepts the exception as its first parameter, so we can do something like:

class MyException extends Exception { }

function guessTheNumber($num) {
	$value = rand(0, 10);
	if ($value === $num) {
	    return true;
    }
    throw new MyException('You guessed the wrong number!');
}

function tryYourLuck() {
	return retry(3, function() {
    	return guessTheNumber(5);
    }, 100, function($exception) {
    	return $exception instanceof MyException;
    });
}

Here we are telling the retry method that it should only retry if the exception if caught was MyException. If it catches any other exception, it should not retry at all.

And with that, I wrap up this blog post. Until next time!