Cancellable Function Usage
Imagine that you have a function full of asynchronous actions:
function ZombieSpawner.SpawnAsync(model, location)
RequestModel(model)
while not HasModelLoaded(model) do Citizen.Wait(100) end
RequestAnimDict('move_m@drunk@verydrunk')
while not HasAnimSetLoaded("move_m@drunk@verydrunk") do Citizen.Wait(100) end
--
end
There is a high chance that you will need to interrupt it if something had happened.
You can’t do it without writing high coupling code, as your method will check itself if it needs to be interrupted:
function ZombieSpawner.SpawnAsync(model, location)
RequestModel(model)
while not HasModelLoaded(model) do
Citizen.Wait(100)
if not GameMode.IsRunning() then return end
end
RequestAnimDict('move_m@drunk@verydrunk')
while not HasAnimSetLoaded("move_m@drunk@verydrunk") do
Citizen.Wait(100)
if not GameMode.IsRunning() then return end
end
--
end
Let’s solve this issue by introducing CancellationToken - basically a wrapper for boolean flag:
CancellationToken = { }
CancellationToken.__index = CancellationToken
function CancellationToken.New()
local self = { }
setmetatable(self, CancellationToken)
self._wasCancelled = false
return self
end
function CancellationToken:Cancel()
self._wasCancelled = true
end
function CancellationToken:WasCancelled()
return self._wasCancelled
end
Let’s declare one more helper:
function Citizen.CancellableWait(ms, token)
Citizen.Wait(ms)
return not token:WasCancelled()
end
And this is how we will use it:
function ZombieSpawner.SpawnAsync(model, location, token)
RequestModel(model)
while not HasModelLoaded(model) do
if not Citizen.CancellableWait(100, token) then return end
end
--
end
Here is a full example:
local cancellationToken = CancellationToken.New()
RegisterNetEvent('gamemode:spawnZombie')
AddEventHandler('gamemode:spawnZombie', function(model, location)
ZombieSpawner.SpawnAsync(model, location, cancellationToken)
end)
RegisterNetEvent('gamemode:removeAllZombies')
AddEventHandler('gamemode:removeAllZombies', function()
cancellationToken:Cancel()
ZombieSpawner.RemoveAll()
end)
I recommend to use cancellation token for async resource loading and I/O operations only.