Unity WebGL对接社交平台API时,异步回调场景下如何正确从函数返回数据?
兄弟,我太懂你现在的头疼之处了——原来用PlayerPrefs是同步拿值,换成社交平台的异步API后,直接同步返回的路子完全走不通,要么拿空值,要么执行顺序全乱。核心问题在于WebGL里Unity和JS的异步交互不能用同步返回的逻辑,得彻底换个思路,下面给你几个落地的解决方案:
一、先把同步接口改成异步回调模式
你原来的IMapProgressManager里的LoadLevelStarsCount是同步返回int,这在异步场景下根本行不通,得先重构接口,让它接受一个回调函数,等数据拿到后再触发后续逻辑:
// 重构后的接口 public interface IMapProgressManager { // 把同步返回改成带回调参数的异步方法 void LoadLevelStarsCount(int level, Action<int> onStarsLoaded); // 其他方法也同理改成异步模式 }
然后修改你的PlayerPrefsMapProgressManager实现,把异步逻辑串起来:
public class PlayerPrefsMapProgressManager : IMapProgressManager { public JSBridgeController bridge; public void LoadLevelStarsCount(int level, Action<int> onStarsLoaded) { // 调用JS获取数据,把回调和关卡键绑定(避免多请求混淆) var levelKey = GetLevelKey(level); bridge.JSStorageGet(levelKey, (value) => { // 拿到JS返回的值后转成int,再触发回调 int stars = int.TryParse(value, out var result) ? result : 0; onStarsLoaded?.Invoke(stars); }); } // 原来的GetLevelKey方法保留 private string GetLevelKey(int level) { return $"Level_{level}_Stars"; } }
调用方也得改成通过回调处理后续逻辑,不能直接拿返回值:
// 比如在UI或游戏逻辑里这样调用 mapProgressManager.LoadLevelStarsCount(1, (stars) => { // 这里拿到正确的星星数后再更新UI或逻辑 Debug.Log($"第1关星星数:{stars}"); // 示例:更新关卡显示 levelUI.SetStars(stars); });
二、修复JS层的异步执行问题
你后来提到JS里的myBridge.send是异步的,原来的_JSStorageGet试图直接return肯定不行——因为then里的逻辑是在C#函数执行完之后才会跑,所以C#那边先拿到了默认值,JS的日志才出来。得把JS里的逻辑改成通过SendMessage把结果推给Unity,而不是返回值:
修改你的jsbridge.jslib:
mergeInto(LibraryManager.library, { _JSStorageSet: function (keyPtr, valuePtr) { var key = UTF8ToString(keyPtr); var value = UTF8ToString(valuePtr); // 调用社交平台的set接口 myBridge.send("StorageSet", {"key": key, "value": value}); }, _JSStorageGet: function (keyPtr) { var key = UTF8ToString(keyPtr); // 异步调用社交平台的get接口 myBridge.send("StorageGet", {"keys": [key]}).then(data => { var resultValue = "0"; // 默认值 if (data.keys[0].value) { resultValue = data.keys[0].value; } // 把结果通过SendMessage传给Unity的JSBridgeHandler ss.SendMessage('JSBridge', 'ResultStorageGet', resultValue); }); } });
然后确保你的JSBridgeController里的JSStorageGet还是用回调存储的逻辑:
public class JSBridgeController : MonoBehaviour { [DllImport("__Internal")] public static extern void _JSStorageSet(string key, string value); [DllImport("__Internal")] public static extern void _JSStorageGet(string key); // 这里改成void,不要返回值 public UnityAction<string> _actionStorageGet; public void JSStorageGet(string key, UnityAction<string> action) { #if !UNITY_EDITOR _actionStorageGet = action; _JSStorageGet(key); #endif } // ...其他代码保留 }
这样JS那边异步拿到数据后,会主动通知Unity触发回调,执行顺序就完全正确了。
三、千万别用“延迟等待”的歪路子
你之前想加个delay等回调,这绝对是坑——异步操作的时间完全不确定,可能网络慢一点延迟就不够,或者网络快了白等,而且WebGL里用协程WaitForSeconds也没法真的阻塞同步函数。彻底放弃同步返回的想法,拥抱异步回调才是正确方向。
备注:内容来源于stack exchange,提问作者Марк

