What is ErrorConsume?
ErrorConsume is basically a pcallBuddy that can also ensure a function doesn’t yield. The module is documented and typed to make it more friendly.
-
ErrorConsume.eatError
- (Calls pcall) Handles any potential error within a function, yielding the current thread until the function concludes. -
ErrorConsume.eatYield
- Handles any potential error within a function, discontinuing the functions execution whenever the function yields.
The usage cases are mainly for developers that create resources for others to use and cannot trust functions as inputs, as they could error or yield when they shouldn’t. For others, you could use it to wrap functions that COULD yield but shouldn’t, like for the callback of UpdateAsync.
Practical Usage:
local ErrorConsume = require("./ErrorConsume")
local dataStore = game:GetService("DataStoreService"):GetDataStore("SampleData")
-- Very similar to game:GetService("MemoryStoreService"):GetHashMap("...")
local memoryStore = require("./Memory")
-- just a wrapper for {Data: any, UserId: {number}, Metadata: {[string]: string}}
local dataObject = require("./DataObject")
local cache = {}
function updateExpansion(key: string, data: dataObject.Object, store: dataObject.Object)
if store == nil then return
data.Data, data.UserId, data.Metadata
end
-- assume both data and store have a metatable with __eq set
if data == store then return nil end
if data == nil then
-- this could pull from a cache or call GetAsync
data = memoryStore:Get(key)
end
--...
end
local currentKey = nil
local currentData = nil
function updateEncloser(store: any, info: DataStoreKeyInfo)
local key = currentKey
local data = currentData
local oldData = dataObject.create(store, info:GetUserIds(), info:GetMetadata())
local success, data, userId, metadata = ErrorConsume.eatYield(function()
return updateExpansion(key, data, oldData)
end)
if success then
return data, userId, metadata
else
return nil
end
end
local success, data, info: DataStoreKeyInfo = ErrorConsume.eatError(function()
currentKey = "Key"
currentData = cache[currentKey]
return dataStore:UpdateAsync(currentKey, updateEncloser)
end)
It isn’t the most performant, and if you know a way to enforce antiyielding without having to close the thread, by all means.
Code to insert into a ModuleScript
local ErrorConsume = {}
local function consumer(f : () -> (), ...)
task.wait()
return f(...)
end
--[[
Handles any potential error within a function, yielding the current thread until the function concludes.
@ Should an error exist, the output will be (false, errorMessage)
@ Should an error not exist, the output will (true, ...) where ... is whatever the function returns
@param f the input function to be handled
@param ... the arguments to pass into the handled function
@returns boolean, ...any
@example
local ErrorConsume = require(...)
local function upper(name : string) : string
return name:upper()
end
ErrorConsume.eatError(upper, Instance.new("Part")) -- Result: false, upper is not a valid member of Part "Part"
]]
ErrorConsume.eatError = function<T...>(f : (...any)->(T...), ...) : (boolean, T...)
return pcall(consumer, f, ...)
end
--[[
Handles any potential error within a function, discontinuing the functions execution whenever the function yields.
@ WARNING: A function may still take seconds or minutes without yielding.
@ Should an error exist, the output will be (false, errorMessage)
@ Should an error not exist, the output will (true, ...) where ... is whatever the function returns
@param f the input function to be handled
@param ... the parameters to pass into the handled function
@returns boolean, ...any
@example
local ErrorConsume = require(...)
local function upper(name : string) : string
task.wait() -- purely for the example, halts here!!
return name:upper()
end
ErrorConsume.eatYield(upper, Instance.new("Part")) -- Result: true
]]
ErrorConsume.eatYield = function<T...>(f : (...any)->(T...), ...) : (boolean, T...)
local coro = coroutine.create(f)
local output = {coroutine.resume(coro, ...)}
coroutine.close(coro) -- cannot yield
return unpack(output)
end
return ErrorConsume