Retry Module | Firework Library

This is a simple module I developed over time for retrying code if it fails. You can provide a function to run, a maximum amount of retries, a wait time between retries, and additional arguments to pass to the function.

pcall() does the job pretty well, but I found myself making retry functions in separate places for different reasons. So, I added this module to my library.

Source Code

Retry - Creator Marketplace (roblox.com)

return function(callback, retries, buffer, ...)
	retries = retries or math.huge
	buffer = buffer or 0
	
	local attempt = 1
	
	while attempt <= retries do
		local results = table.pack(pcall(callback, ...))
		local success = table.remove(results, 1)
		local result = results[1]
		
		if success then
			return table.unpack(results)
		else
			warn(debug.traceback(result))
		end
		
		attempt += 1
		
		task.wait(buffer)
	end
end 

Usage

Here’s an example of calling SetAsync on a data store with 3 retries and 1 second between retries.

Retry(function()
	store:SetAsync(player.UserId, value)
end, 3, 1)

If you wanted to compact it down, you can use additional arguments to call SetAsync as a static method with “store” as self.

Retry(store.SetAsync, 3, 1, store, player.UserId, value)

You can also get back anything you return in the function. Not providing a value for max retries will repeat forever until it succeeds. Not providing a value for buffer will default to 0.

function Example()
	return 1, 2
end

local resultA, resultB = Retry(Example)

That’s about it!

11 Likes

pretty neat

just curious, how does this compare to using something like promise.retry?

regardless, your module can still be very useful

repeat until pcall(function()
    print("Run")
    if math.random() < 0.75 then error() end
end)

image

3 Likes

This is a pretty nice and compact retry, but I like having the max retries option. For example, it wouldn’t make sense to continue retrying saving player data forever and exhaust rate limits for the whole server, maybe a handful of attempts. And if you wanted to do a buffer this way, you would have to yield at the end of the function, even if it succeeds. This is helpful for staying within rate limits that most async calls come with.

From what I can find, it looks almost identical. It should be very similar in practice.

1 Like

I did some tests with a few modifications in InCommand as well as the max retries option

image

also here is the code

print("InCommand Test: bluebxrrybot's retry function") -- unneccesary but placed so there won't be `Run (x50)` from that
local maxRetries = 10
local retries = 0

repeat pcall(function()
    retries += 1
    print("Run")
    if math.random() < 0.75 then error() end
end) until retries == maxRetries