我在 Flutter 中有一个服务类,它使用 Rest API 调用从服务器获取对象。它会缓存这些对象,并在 UI 尝试再次获取同一对象时返回缓存的对象。像这样:
Future<User?> getUser(int userid) async{
User? u = _userCache.get(userid);
if(u == null) {
ApiResponse<User> ar = await _restapi.getUser(userid);
if (ar.successful && ar.data != null) {
u = ar.data!;
_userCache.add(userid,u);
}
}
return u;
}
现在,我遇到的问题如下:UI 在构建屏幕时多次需要相同的 User 对象,因此它多次调用 getUser(id) 。理想情况下,它应该为第二个和后续请求返回缓存的 User,但对服务器的第一个请求尚未完成,并且缓存没有 User 对象(即使几秒钟后它将拥有该对象)。这会导致应用程序多次调用 Rest API 调用。
我基本上只想对 RestAPI 进行一次活动/挂起的调用。像这样的东西:
Future<User?> getUser(int userid) async{
>>>> CHECK whether a call to backend is pending. If so, then wait here.<<<<
User? u = _userCache.get(userid); <--This call should now succeed
...same as before
}
这是应该这样做吗?实现这一目标的正确方法是什么?
======更新=================
根据@dangngocduc的建议,我的 getUser 方法现在看起来像这样:
Future<User?> getUser(int userid) async{
User? u = _userCache.get(userid);
if(u == null) {
await userLock.synchronized( () async {
u = _userCache.get(userid);
if(u == null) {
ApiResponse<User> ar = await _uome.getUser(userid);
if (ar.successful && ar.data != null) {
u = ar.data!;
_userCache.add(userid, u!);
}
}});
}
return u;
}
可以通过制作一个映射/字典来跟踪您正在使用的每个数据模型的上次获取时间来解决此问题,例如:
然后在获取特定模型之前你应该检查最后一次获取时间,如果是
null
或者已经足够长了(我通常设置为5秒),那么你应该调用API,否则就忽略并返回,假设还有另一个异步调用正在进行。
class OdooDataController extends GetxController {
final _users = <User>[].obs;
final fetchDates = <String, DateTime>{}.obs;
final modelMap = {
'users': User,
};
RxList<User> get users {
maybeFetchUsers();
return _users;
}
void maybeFetchUsers({forceFetch = false}) async {
const model = 'users';
var lastFetch = fetchDates[model];
var shouldFetch = lastFetch == null; // if lastFetch is null, we should fetch, otherwise we need to check the time below
const fetchLimit = 5; // 5 seconds is reasonlable for most cases
if (lastFetch != null &&
(DateTime.now().difference(lastFetch!).inSeconds >= fetchLimit)) {
shouldFetch = true; // here we made sure that the last fetch was a long time ago
}
if (shouldFetch || forceFetch) {
fetchDates[model] = DateTime.now();
_users.value = await fetchModel<User>(model, User.fields);
}
}
}
此解决方案并不适用于所有情况。你需要看看它是否适合你。
此外,您可能希望为您正在使用的每个模型设置不同的
fetchLimit
值。例如,如果您获取城市列表,您可能希望将 fetchLimit
设置为大于 5 秒,因为您不希望该列表经常更改。另外,如果您正在获取的数据非常大,下载时间超过 5 秒,也许您想增加限制。