MST

星途 面试题库

面试题:Flutter中复杂异步数据加载场景下Future的优化策略与最佳实践

在一个复杂的Flutter应用中,可能存在多个Future并行或串行执行以加载不同来源的数据,同时还需要处理数据依赖关系以及避免资源浪费等问题。请描述针对这种复杂异步数据加载场景,你会采用哪些优化策略和最佳实践,包括但不限于Future的并发控制、缓存机制的引入等,并结合具体业务场景给出详细的代码实现思路。
45.7万 热度难度
前端开发Flutter

知识考点

AI 面试

面试题答案

一键面试

1. Future的并发控制

  • 并行执行: 使用Future.wait方法来并行执行多个Future。例如,在一个新闻应用中,需要同时加载新闻列表和用户设置。
Future<List<dynamic>> loadData() async {
  Future<List<News>> newsFuture = fetchNews();
  Future<UserSettings> settingsFuture = fetchUserSettings();
  List<dynamic> results = await Future.wait([newsFuture, settingsFuture]);
  return results;
}
  • 串行执行: 使用async*yield来串行执行Future。比如在一个需要依次下载多个文件的场景中。
Stream<File> downloadFiles(List<String> urls) async* {
  for (String url in urls) {
    File file = await downloadFile(url);
    yield file;
  }
}

2. 缓存机制的引入

  • 内存缓存: 使用Map来实现简单的内存缓存。假设我们有一个获取用户信息的接口,并且在短时间内可能会多次调用。
Map<String, User> userCache = {};
Future<User> getUser(String userId) async {
  if (userCache.containsKey(userId)) {
    return userCache[userId];
  }
  User user = await fetchUserFromServer(userId);
  userCache[userId] = user;
  return user;
}
  • 持久化缓存: 使用shared_preferenceshive等库进行持久化缓存。以存储用户登录信息为例,使用shared_preferences
import 'package:shared_preferences/shared_preferences.dart';

Future<bool> saveUserLoginInfo(String token) async {
  SharedPreferences prefs = await SharedPreferences.getInstance();
  return prefs.setString('user_token', token);
}

Future<String?> getUserLoginInfo() async {
  SharedPreferences prefs = await SharedPreferences.getInstance();
  return prefs.getString('user_token');
}

3. 处理数据依赖关系

  • 链式调用: 如果Future之间存在依赖关系,可以使用链式调用。例如,在一个电商应用中,先获取商品列表,然后根据商品ID获取商品详情。
Future<ProductDetail> getProductDetail() async {
  List<Product> products = await fetchProductList();
  Product firstProduct = products.first;
  return fetchProductDetailById(firstProduct.id);
}
  • 使用Completer: 当需要手动控制Future的完成时,可以使用Completer。比如在一个需要等待多个异步操作完成后再触发某个操作的场景。
Completer<void> completer = Completer<void>();
Future<void> performComplexOperation() async {
  Future<void> operation1 = performOperation1();
  Future<void> operation2 = performOperation2();
  Future.wait([operation1, operation2]).then((_) {
    completer.complete();
  });
  return completer.future;
}

4. 避免资源浪费

  • 取消Future: 使用CancelTokenFuturecancel方法(如果支持)来取消未完成的Future。例如,在一个搜索功能中,用户频繁输入搜索关键词,前一个搜索请求如果还未完成,可以取消。
import 'package:http/http.dart' as http;
import 'package:http_cancel/http_cancel.dart';

CancelToken cancelToken = CancelToken();
Future<SearchResults> search(String query) async {
  try {
    http.Response response = await http.get(Uri.parse('$baseUrl/search?q=$query'),
        cancelToken: cancelToken);
    return SearchResults.fromJson(response.body);
  } on CancelException {
    // 这里可以处理取消异常
    return null;
  }
}
  • 节流和防抖: 在触发异步操作时,使用节流和防抖策略。比如在一个自动加载更多的列表中,使用防抖避免短时间内多次触发加载更多操作。
import 'dart:async';

Debouncer debouncer = Debouncer(milliseconds: 500);
void loadMore() {
  if (debouncer.canCall()) {
    // 执行加载更多逻辑
    fetchMoreData();
  }
}

class Debouncer {
  final int milliseconds;
  Timer? _timer;

  Debouncer({required this.milliseconds});

  bool canCall() {
    if (_timer?.isActive?? false) {
      _timer?.cancel();
    }
    _timer = Timer(Duration(milliseconds: milliseconds), () {});
    return true;
  }
}