Flutter 异步与同步(1):异步与同步机制(async、await 和 Future)

在 Flutter/Dart 中,asyncawait 是处理异步编程的核心关键字,它们可以让你以同步的方式编写异步代码,显著提高代码的可读性和可维护性。

一、同步/异步的理解

await:同步机制

同步操作会阻止其他操作执行,直到完成为止。同步就好比打电话一样,打电话时都是一个人在说另一个人听,一个人在说的时候另一个人等待,等另一个人说完后再接着说。

Flutter_async_A.png


async:异步机制
异步操作一旦启动,就允许其他操作在其完成之前执行。异步就好比发邮件一样,收件人和发件人都不需要相互等待,发件人写完邮件的时候简单的点个发送就可以了,收件人收到后就可以阅读啦,收件人和发件人不需要相互依赖、不需要相互等待。

Flutter_async_A.png


二、Dart 的异步模型

Flutter 的 UI 渲染依赖于 主线程(UI 线程) ,如果任务执行时间过长,会导致界面卡顿(掉帧)。为了保证流畅性,Flutter 采用 异步模型 处理耗时任务,比如:

  • 网络请求
  • 文件读写
  • 数据库操作
  • 动画
  • I/O 操作

Dart 的单线程模型

Dart 采用 单线程事件循环,与 JavaScript 类似:

  • 事件按照 先进先出(FIFO) 执行
  • 任务分为:
    • 微任务(Microtask) :优先执行(类似 JS 的 Promise.then
    • 事件任务(Event Task) :比如 I/O、鼠标点击、定时器等

三、Future 的基本概念

(1)核心关键字

  • async: 标记函数为异步函数
  • await: 等待异步操作完成
  • Future: 表示可能在未来完成的操作

(2)基本语法

Future<void> fetchData() async {
  // 同步代码
  print('开始获取数据');
  
  // 异步操作
  var data = await http.get('https://api.example.com/data');
  
  // 等待完成后执行
  print('获取到数据: $data');
}

(3)引入 async/await 的原因

在 Dart 中,Future 代表 一个异步操作的结果

Future<String> fetchData() {
  return Future.delayed(Duration(seconds: 2), () => "数据加载完成");
}

需要使用 .then() 处理,获取 fetchData 这个异步操作的结果数据,然后打印该数据:

fetchData().then((data) {
  print(data);
});

.then() 方式 嵌套太多,会变得难以阅读(回调地狱):

fetchData().then((data) {
  process(data).then((result) {
    save(result).then((_) {
      print("完成");
    });
  });
});

为了解决这个问题,Dart 引入了 async/await

四、async/await 的工作原理

async 和 await 关键字构成了异步编程的基础,它们极大地简化了异步操作的处理流程。async 关键字用于声明一个函数为异步函数,这意味着该函数的执行可能不会立即完成,并且会返回一个 Future 对象。

  • Future 是 Dart 中的一个核心类,代表一个可能在未来某个时间点返回结果的计算。当在 async 函数内部遇到 await 关键字时,执行流程会暂停,直到 await 后面的异步操作完成。这种暂停并不会阻塞整个线程,而是允许 Dart 的事件循环继续处理其他事件,如用户交互或动画,从而保持应用的响应性。

  • 使用 await 的优势在于它能够让异步代码的逻辑流程更加直观和清晰。开发者不再需要通过嵌套回调(俗称“回调地狱”)来处理异步操作的结果,而是可以以近似同步的方式编写代码,使得逻辑更容易理解和维护。

  • async 和 await 在错误处理方面也提供了便利。在 async 函数中,可以使用传统的 try-catch 结构来捕获异步操作中抛出的异常,这比处理 Future 的 catchError 方法更加直观。


使用 async/await 让代码更直观

Future<String> fetchData() {
  return Future.delayed(Duration(seconds: 2), () => "数据加载完成");
}

Future<void> loadData() async {
  String data = await fetchData(); // 等待 await 后面的 fetchData 这个异步操作执行完,再获取结果数据
  print(data);
}

等价于:

fetchData().then((data) {
  print(data);
});

async/await 只是 Future 的语法糖,它不会改变 Future 本身的行为,而是让异步代码像同步代码一样编写。

五、事件循环(Event Loop)与任务调度

Flutter 运行时采用 事件循环(Event Loop) 处理异步任务:

  • 同步任务 立即执行
  • 微任务(Microtask) 在当前事件循环执行完毕后立即执行
  • 事件任务(Event Task) 交给事件队列,等待空闲时执行

Dart 采用 单线程事件循环 处理所有任务,执行顺序

  • 先执行 同步任务
  • 再执行 所有微任务 (特点有:优先级高,比事件任务先执行;一次性执行完所有微任务;不能主动终止;适用于 短小的任务 任务调度(State 更新)(日志收集)(UI 细节调整))
  • 最后执行 一个事件任务(特点有:事件任务 适用于 IO、定时器、用户交互等任务,优先级低于微任务,如用户输入(点击、键盘事件))
  • 然后继续执行微任务(如果有)

示例:

void main() {
  print("1");
  Future(() => print("2"));
  scheduleMicrotask(() => print("3"));
  print("4");
}

执行顺序:

  1. print("1")
  2. print("4")(同步任务执行完毕)
  3. print("3")(微任务队列执行)
  4. print("2")(事件任务执行)

六、await 背后的状态机

await 并不会阻塞线程,而是将代码拆分为多个 Future 回调,然后利用 状态机 进行调度。

示例

Future<void> main() async {
  print("A");
  await Future.delayed(Duration(seconds: 1));
  print("B");
}

等价代码

Future<void> main() {
  print("A");
  return Future.delayed(Duration(seconds: 1)).then((_) {
    print("B");
  });
}

执行过程

  1. print("A")
  2. Future.delayed 被加入 事件队列
  3. 主线程任务执行完毕,事件循环开始处理 Future
  4. print("B")

await 关键字 会拆分成多个 Future 回调,每个 await 相当于创建了一个新的 Future.then

七、async/await 源码解析

在 Dart 源码中,async 代码会被编译成 Future 回调链,并使用 状态机 控制执行。

查看 dart:async 中的 Future 代码:

Future<T> async<T>(FutureOr<T> Function() computation) {
  return Future<T>(() {
    return computation();
  });
}

Dart 运行时会自动将 async 方法转换成 Future 状态机。

示例

Future<int> compute() async {
  int result = await Future.value(10);
  return result;
}

实际执行时,相当于:

Future<int> compute() {
  return Future.value(10).then((result) {
    return result;
  });
}

Dart 运行时 通过状态机调度 await 关键字,使其执行时不会阻塞主线程

八、async/await 使用

(1)声明异步函数

任何返回 Future 的函数都应该标记为 async:

Future<String> getUserName() async {
  // ...
}

(2)等待单个 Future

Future<int> getAge() async {
  await Future.delayed(Duration(seconds: 1)); // 模拟延迟
  return 25;
}

void printAge() async {
  int age = await getAge();
  print('年龄: $age');
}

(3)处理多个 Future

并行执行

Future<void> fetchAllData() async {
  // 同时启动所有异步操作
  Future<String> userFuture = getUser();
  Future<String> postsFuture = getPosts();
  Future<String> commentsFuture = getComments();
  
  // 等待所有完成
  final results = await Future.wait([userFuture, postsFuture, commentsFuture]);
  
  print('用户: ${results[0]}');
  print('帖子: ${results[1]}');
  print('评论: ${results[2]}');
}

顺序执行

Future<void> fetchSequentially() async {
  final user = await getUser();
  final posts = await getPosts(user.id);
  final comments = await getComments(posts.first.id);
  
  print('完整数据链: $user -> $posts -> $comments');
}

(4)处理异常

使用 try-catch 捕获 async/await 异常:

// 示例1
Future<void> fetchData() async {
  try {
    String data = await Future.error("网络异常");
    print(data);
  } catch (e) {
    print("捕获异常: $e");
  }
}

// 示例2
Future<void> loadData() async {
  try {
    var data = await fetchData();
    print('数据加载成功: $data');
  } catch (e) {
    print('发生错误: $e');
  } finally {
    print('加载操作完成');
  }
}

避免未处理的异常导致应用崩溃

九、Flutter 中的实际应用

(1)网络请求

Future<List<Post>> fetchPosts() async {
  final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts'));
  
  if (response.statusCode == 200) {
    return (jsonDecode(response.body) as List)
      .map((post) => Post.fromJson(post))
      .toList();
  } else {
    throw Exception('加载失败');
  }
}

(2)文件操作

Future<void> saveFile(String content) async {
  final file = File('document.txt');
  await file.writeAsString(content);
  print('文件保存成功');
}

(3)数据库访问

Future<List<User>> getUsers() async {
  final db = await DatabaseHelper.instance.database;
  final maps = await db.query('users');
  return maps.map((map) => User.fromMap(map)).toList();
}

(4)结合 setState 使用

Future<void> loadAndRefresh() async {
  setState(() => isLoading = true);
  try {
    data = await fetchData();
  } catch (e) {
    errorMessage = e.toString();
  } finally {
    setState(() => isLoading = false);
  }
}

十、常见问题解决方案

(1)避免 await 阻塞 UI

问题代码

void main() async {
  await Future.delayed(Duration(seconds: 3)); // UI 会卡顿
  runApp(MyApp());
}

改进方案

void main() {
  runApp(MyApp());
  Future.delayed(Duration(seconds: 3), () {
    print("初始化完成");
  });
}

(2)避免嵌套地狱

问题代码

Future<void> nestedHell() async {
  await func1().then((r1) async {
    await func2(r1).then((r2) async {
      await func3(r2).then((r3) {
        print(r3);
      });
    });
  });
}

改进方案

Future<void> cleanCode() async {
  final r1 = await func1();
  final r2 = await func2(r1);
  final r3 = await func3(r2);
  print(r3);
}

(3)正确处理 async/await 与 then 混用

// 不推荐
Future<void> badPractice() async {
  await future.then((value) => process(value));
}

// 推荐
Future<void> goodPractice() async {
  final value = await future;
  process(value);
}

(4)避免不必要的 async

// 不必要
Future<int> unnecessaryAsync() async {
  return 42;
}

// 改进
Future<int> betterVersion() {
  return Future.value(42);
}

九、结论

  • async/await 只是 Future 的语法糖,本质是 状态机 + Future.then()
  • await 不会阻塞 UI 线程
  • Dart 采用 事件循环 处理异步任务
  • 微任务(Microtask)优先于事件任务(Event Task)
  • 优化:并行 await,避免 UI 阻塞

理解 async/await 的底层原理,有助于编写更高效、可维护的 Flutter 代码


参考:

Flutter中同步与异步_flutter async-CSDN博客

详细解释Flutter中 async/await 的工作原理在 Flutter(Dart)中,async/await 是 - 掘金


posted @ 2025-05-30 14:41  fengMisaka  阅读(23)  评论(0)    收藏  举报