位置:首页 > Dart > Dart异步编程基础入门:Future与async/await详解

Dart异步编程基础入门:Future与async/await详解

时间:2026-08-21  |  作者:实验室老王  |  阅读:0

回顾此前的学习,咱们编写的代码基本上都是同步执行的。代码会按顺序从上到下依次运行,只有前一行执行完毕,才会继续执行下一行。

但在实际开发中,很多操作都需要等待外部响应,比如网络请求、文件读写等。要是采用同步方式处理,程序就很容易“卡死”。

异步编程正是解决这类问题的核心技术。它在UI程序(像Flutter应用等)中,起着至关重要的作用。

dart学习第 13 节:异步编程基础 —— Future 与 async/await

一、同步 vs 异步:为什么 UI 程序离不开异步?

1. 同步执行的局限性

同步代码的执行流程像一条直线,每一步必须等待上一步完成

void main() {
  print("开始执行");
  print("执行耗时操作...");
  // 模拟一个耗时 3 秒的操作(如网络请求)
  for (int i = 0; i < 1000000000; i++) {}
  print("操作完成");
  print("继续执行其他任务");
}

// 输出顺序:
// 开始执行
// 执行耗时操作...
// (等待 3 秒)
// 操作完成
// 继续执行其他任务

这种模式在耗时操作面前会出现严重问题,比如网络请求、大数据处理。

  • 整个程序会被阻塞,无法响应用户操作,如点击按钮、滑动屏幕
  • UI 会冻结,给用户带来“卡死”的糟糕体验

2. 异步执行的优势

异步代码的核心是:耗时操作在“后台”执行,主线程继续处理其他任务,待耗时操作完成后再回调处理结果

可以用生活场景来理解:

  • 同步:烧开水时一直盯着水壶,什么也不做,水开了再倒水泡茶。
  • 异步:烧开水时先去准备茶叶和杯子,水开了再回来倒水泡茶。

在 UI 程序中,异步是刚需:

  • 保证 UI 线程不被阻塞,始终能响应用户操作
  • 提升程序运行效率,多个任务可“并行”处理

二、Future:异步操作的“占位符”

Dart 中用 Future 表示一个“未来会完成的操作”。它是异步操作的“占位符”。

创建时我们不知道结果,但知道未来某个时刻会得到结果,可能成功,也可能失败。

1. Future 的三种状态

  • 未完成(pending):异步操作正在执行,结果尚未产生
  • 已完成(completed with value):异步操作成功,返回结果值
  • 已完成(completed with error):异步操作失败,返回错误信息

2. 创建 Future 与处理结果

可以通过 Future 构造函数创建异步操作,并用 then 处理成功结果,用 catchError 处理错误:

void main() {
  print("开始执行主线程任务");

  // 创建一个 Future(异步操作)
  Future<String> fetchData() {
    // 模拟网络请求,2 秒后返回结果
    return Future.delayed(Duration(seconds: 2), () {
      // 模拟成功场景
      return "获取到的数据:Dart 异步编程";

      // 模拟失败场景(取消上面一行注释,启用下面一行)
      // throw Exception("网络错误,获取数据失败");
    });
  }

  // 调用异步函数,得到 Future 对象
  Future<String> future = fetchData();

  // 注册回调:当 Future 成功完成时执行
  future
      .then((data) {
        print("异步操作成功:$data");
      })
      .catchError((error) {
        // 注册回调:当 Future 失败时执行
        print("异步操作失败:${error.toString()}");
      })
      .whenComplete(() {
        // 注册回调:无论成功或失败,最终都会执行
        print("异步操作结束(无论成败)");
      });

  print("主线程继续执行其他任务");
}

// 输出顺序(成功场景):
// 开始执行主线程任务
// 主线程继续执行其他任务
// (等待 2 秒)
// 异步操作成功:获取到的数据:Dart 异步编程
// 异步操作结束(无论成败)

关键特点:

  • 调用 fetchData() 后,主线程不会等待,会直接执行下一行 print
  • 2 秒后异步操作完成,才会执行 then 中的回调函数
  • catchError 捕获异步操作中抛出的错误
  • whenComplete 类似“finally”,无论成败都会执行

3. Future.value 与 Future.error

如果要快速创建“已完成”的 Future,可以直接使用下面两种方式:

void main() {
  // 直接创建一个成功的 Future
  Future.value("直接返回成功结果").then((data) {
    print(data); // 输出:直接返回成功结果
  });

  // 直接创建一个失败的 Future
  Future.error(Exception("直接返回错误")).catchError((error) {
    print(error); // 输出:Exception: 直接返回错误
  });
}

三、async/await:用同步风格写异步代码(重点)

虽然 then 链式调用可以处理异步,但多层嵌套时容易导致“回调地狱”,代码会变得臃肿,也难以维护。

Dart 提供了 async/await 语法糖,让我们能用同步代码的写法来编写异步逻辑。

1. 基本用法

  • async:修饰函数,表明该函数是异步的,返回值会自动包装为 Future
  • await:只能在 async 函数中使用,等待 Future 完成并获取结果
void main() {
  print("开始执行主线程任务");

  // 调用异步函数(无需等待,主线程继续执行)
  fetchAndPrintData();

  print("主线程继续执行其他任务");
}

// 用 async 修饰,表明这是异步函数
Future<void> fetchAndPrintData() async {
  try {
    print("开始异步获取数据");

    // 用 await 等待 Future 完成,获取结果(类似同步代码的写法)
    String data = await fetchData(); // 等待 fetchData() 完成

    // 上面的 await 会“暂停”当前函数,直到 Future 完成
    print("异步操作成功:$data");
  } catch (error) {
    // 捕获异步操作中的错误(替代 catchError)
    print("异步操作失败:${error.toString()}");
  } finally {
    // 无论成功失败都会执行(替代 whenComplete)
    print("异步操作结束(无论成败)");
  }
}

// 模拟网络请求的异步函数
Future<String> fetchData() {
  return Future.delayed(Duration(seconds: 2), () {
    return "获取到的数据:async/await 真方便";
    // 模拟失败:throw Exception("网络错误");
  });
}

// 输出顺序:
// 开始执行主线程任务
// 主线程继续执行其他任务
// 开始异步获取数据
// (等待 2 秒)
// 异步操作成功:获取到的数据:async/await 真方便
// 异步操作结束(无论成败)

2. 为什么 async 函数返回 Future

  • async 修饰的函数,其返回值会被自动包装为 Future
  • 若函数返回 int,实际返回类型是 Future
  • 若函数无返回值,实际返回类型是 Future
// 返回 Future
Future<int> calculate() async {
  await Future.delayed(Duration(seconds: 1));
  return 100; // 自动包装为 Future.value(100)
}

void main() async {
  int result = await calculate(); // 用 await 获取结果
  print(result); // 输出:100
}

3. 处理多个异步操作

async/await 让多个异步操作的顺序执行变得更简单:

// 模拟三个异步操作
Future<String> fetchUser() =>
    Future.delayed(Duration(seconds: 1), () => "用户信息");
Future<String> fetchOrders() =>
    Future.delayed(Duration(seconds: 1), () => "订单列表");
Future<String> fetchRecommendations() =>
    Future.delayed(Duration(seconds: 1), () => "推荐商品");

// 顺序执行多个异步操作
Future<void> fetchAllData() async {
  print("开始获取所有数据...");

  // 按顺序执行,总耗时 ~3 秒
  String user = await fetchUser();
  print("获取到:$user");

  String orders = await fetchOrders();
  print("获取到:$orders");

  String recommendations = await fetchRecommendations();
  print("获取到:$recommendations");

  print("所有数据获取完成");
}

void main() {
  fetchAllData();
}

若多个异步操作无依赖关系,可并行执行。此时可以使用 Future.wait

Future<void> fetchAllDataParallel() async {
  print("开始并行获取所有数据...");

  // 并行执行,总耗时 ~1 秒(取最长的单个操作时间)
  ListString>> futures = [
    fetchUser(),
    fetchOrders(),
    fetchRecommendations(),
  ];

  // 等待所有 Future 完成,返回结果列表
  List<String> results = await Future.wait(futures);

  for (String result in results) {
    print("获取到:$result");
  }

  print("所有数据获取完成");
}

四、异步编程常见陷阱

1. 忘记使用 await

Future<int> getNumber() async => 42;

void main() async {
  // 错误:忘记用 await,得到的是 Future 而不是结果
  var result = getNumber();
  print(result); // 输出:Instance of 'Future'

  // 正确:用 await 获取结果
  var correctResult = await getNumber();
  print(correctResult); // 输出:42
}

2. 在非 async 函数中使用 await

// 错误:await 只能在 async 函数中使用
void badFunction() {
  // await Future.delayed(Duration(seconds: 1)); // 编译错误
}

// 正确:用 async 修饰函数
void goodFunction() async {
  await Future.delayed(Duration(seconds: 1)); // 正确
}

3. 未处理异常导致程序崩溃

Future<void> riskyOperation() async {
  throw Exception("意外错误"); // 抛出异常
}

void main() async {
  // 错误:未处理异常,会导致程序崩溃
  // await riskyOperation();

  // 正确:用 try-catch 处理异常
  try {
    await riskyOperation();
  } catch (e) {
    print("捕获到异常:$e"); // 安全处理
  }
}

五、实际应用场景

异步编程在实际开发中无处不在,下面是几个常见场景:

1. 网络请求

Future fetchUserInfo(String userId) async {
  final response = await http.get(
    Uri.parse("https://api.example.com/users/$userId"),
  );
  if (response.statusCode == 200) {
    return User.fromJson(json.decode(response.body));
  } else {
    throw Exception("Failed to load user");
  }
}

2. 本地存储操作

Future<void> sa veData(String key, String value) async {
  final prefs = await SharedPreferences.getInstance();
  await prefs.setString(key, value);
}

3. 延迟执行

Future<void> showSplashScreen() async {
  print("显示启动页");
  await Future.delayed(Duration(seconds: 3)); // 等待 3 秒
  print("关闭启动页,进入首页");
}

免责声明:文中图文均来自网络,如有侵权请联系删除,心愿游戏发布此文仅为传递信息,不代表心愿游戏认同其观点或证实其描述。

相关文章

更多

精选合集

更多

大家都在玩

热门话题

大家都在看

更多