I've found there's two sources of delay, especially in Classic: (a) asynchronously querying the localized spell name; and (b) waiting for the spell book to be available at PLAYER_LOGIN.
I quickly wrote the snippet below to convert spellIDs into localized names, but not to check the spell book until at least PLAYER_LOGIN and possibly later if asynchronous queries return info even later. This snippet also deletes non-existing spellIDs, which could happen for classic vs. retail differences.
If you are creating click-cast frames for use during combat, then you should change this to do as much as possible at PLAYER_LOGIN even if some spells are still slow loading -- its possible the player is logging in during a raid fight after the game crashed, and they will need to rejoin the fight on a best-effort basis before combat lockdown kicks in.
Lua Code:
local spells = {1459, 23028, 1008, 604} -- mage buffs
local spellsLoaded = 0
local function updateSpells()
if (spellsLoaded == #spells) then
for __, spellName in ipairs(spells) do
if (GetSpellInfo(spellName)) then
-- doSomething()
else
-- doSomethingElse()
end
end
end
end
local playerIsLoggedIn = false
local f = CreateFrame("Frame")
f:RegisterEvent("PLAYER_LOGIN")
f:SetScript("OnEvent", function()
updateSpells()
playerIsLoggedIn = true
f:RegisterEvent("LEARNED_SELL_IN_TAB")
pcall(f.RegisterEvent, f, "ACTIVE_TALENT_GROUP_CHANGED") -- retail only
f:SetScript("OnEvent", updateSpells)
end
local i = 1
while spells[i] do
local entry = i -- This local reference avoids race conditions between the loop and async queries.
if (C_Spell.DoesSpellExist(entry)) then
local spell = Spell:CreateFromSpellID(entry)
spell:ContinueOnSpellLoad(function()
spells[entry] = GetSpellInfo(spells[entry])
spellsLoaded = spellsLoaded + 1
if (playerLoginHappened) then
updateSpells()
end
end)
i = i + 1 -- moves to the next spell
else
-- The spell doesn't exist; maybe because of classic vs retail?
tremove(spells, i) -- shifts the next spell forward
end
end