最近我写了个音乐播放器插件,其中有一个功能是需要通过服务器请求 API 获取音乐信息,这个功能我是通过 ESX Server Callback 实现的,简单来说代码大概就是这样:
客户端
ESX.TriggerServerCallback("musicplayer:getMusicUrl", function(result)
print("Music URL:", result)
-- 这里进行其他处理
end, musicId)
服务端
ESX.RegisterServerCallback("musicplayer:getMusicUrl", function(source, cb, musicId)
local result = nil
PerformHttpRequest(string.format("https://api.example.com/?id=%d", id), function(errorCode, resultData, resultHeaders)
if errorCode == 200 then
result = resultData
else
result = false
end
end)
while result == nil do
Citizen.Wait(0)
end
print("执行完成")
cb(result)
end)
咋一看很正常对吧?从 API 获取数据,然后等待完成,完成之后回调给客户端
但是,有一点被忽略了,就是 Server Callback 它并非是异步网络事件,它会阻塞当前网络通信,也就是如果你在 Callback 里面进行长时间的 Wait,其他的网络事件将会被阻塞,导致新的数据包进不来,时间一长,玩家就会被判定超时然后离线。
那么经过我查阅文档,其实 Server Callback 的 cb 可以在 function 已经完全被执行完之后,再从另一个协程进行调用的,也就是说,即使当前事件已经执行到 print("执行完成")
这一句后面了,但是 cb 此时依然是可以回调的状态,因此可以在 PerformHttpRequest 请求已经完全返回之后再进行调用。
正确的修改后代码如下:
ESX.RegisterServerCallback("musicplayer:getMusicUrl", function(source, cb, musicId)
PerformHttpRequest(string.format("https://api.example.com/?id=%d", id), function(errorCode, resultData, resultHeaders)
if errorCode == 200 then
cb(resultData)
else
cb(false)
end
end)
end)
这样就不会阻塞服务器主线程了,也可以避免因为长时间占用网络事件而导致掉线。