Flutter的异步编程详细

在 Dart 中,FutureStream 是处理异步编程的核心工具。Future 用于处理单个异步操作的结果,而 Stream 用于处理一系列异步事件。为了更深入地理解它们,我们将从基础到高级用法详细讲解,并提供丰富的实例。


1. Future

Future 表示一个异步操作的结果,它可能成功(返回一个值)或失败(抛出异常)。

1.1 Future 的基本用法

创建 Future

javascript 复制代码
Future<String> fetchUserData() {
  return Future.delayed(Duration(seconds: 2), () => "User Data");
}

使用 thencatchError

scss 复制代码
void main() {
  fetchUserData().then((data) {
    print("Data received: $data");
  }).catchError((error) {
    print("Error: $error");
  });
}

使用 async/await

dart 复制代码
void main() async {
  try {
    String data = await fetchUserData();
    print("Data received: $data");
  } catch (error) {
    print("Error: $error");
  }
}

1.2 Future 的常用方法

whenComplete

无论 Future 成功还是失败,都会执行回调。

scss 复制代码
void main() {
  fetchUserData()
      .then((data) => print("Data received: $data"))
      .catchError((error) => print("Error: $error"))
      .whenComplete(() => print("Operation complete"));
}

Future.wait

等待多个 Future 完成。

dart 复制代码
void main() async {
  List<Future<String>> futures = [
    Future.delayed(Duration(seconds: 2), () => "Data 1"),
    Future.delayed(Duration(seconds: 3), () => "Data 2"),
    Future.delayed(Duration(seconds: 1), () => "Data 3"),
  ];

  List<String> results = await Future.wait(futures);
  print("All data received: $results");
}

Future.timeout

设置 Future 的超时时间。

dart 复制代码
void main() async {
  try {
    String data = await fetchUserData().timeout(Duration(seconds: 1));
    print("Data received: $data");
  } on TimeoutException {
    print("Timeout occurred");
  } catch (error) {
    print("Error: $error");
  }
}

1.3 Future 的高级用法

链式调用

javascript 复制代码
void main() {
  fetchUserData()
      .then((data) => data.toUpperCase())
      .then((data) => print("Uppercased Data: $data"))
      .catchError((error) => print("Error: $error"));
}

自定义 Future

csharp 复制代码
Future<int> customFuture() {
  return Future(() {
    // 模拟耗时操作
    return 42;
  });
}

void main() async {
  int result = await customFuture();
  print("Result: $result");
}

2. Stream

Stream 表示一系列异步事件,它可以发出多个值、错误或完成信号。

2.1 Stream 的基本用法

创建 Stream

arduino 复制代码
Stream<int> countStream(int max) async* {
  for (int i = 1; i <= max; i++) {
    await Future.delayed(Duration(seconds: 1));
    yield i;
  }
}

使用 listen

dart 复制代码
void main() {
  countStream(5).listen(
    (data) => print("Data: $data"),
    onError: (error) => print("Error: $error"),
    onDone: () => print("Stream completed"),
  );
}

2.2 Stream 的常用方法

map

Stream 中的每个事件转换为另一个值。

javascript 复制代码
void main() {
  countStream(5)
      .map((data) => data * 2)
      .listen((data) => print("Mapped Data: $data"));
}

where

过滤 Stream 中的事件。

javascript 复制代码
void main() {
  countStream(5)
      .where((data) => data % 2 == 0)
      .listen((data) => print("Filtered Data: $data"));
}

takeskip

  • take(n):只取前 n 个事件。
  • skip(n):跳过前 n 个事件。
scss 复制代码
void main() {
  countStream(5)
      .take(3)
      .listen((data) => print("Taken Data: $data"));

  countStream(5)
      .skip(2)
      .listen((data) => print("Skipped Data: $data"));
}

reduce

Stream 中的所有事件合并为一个值。

javascript 复制代码
void main() {
  countStream(5)
      .reduce((a, b) => a + b)
      .then((sum) => print("Sum: $sum"));
}

2.3 Stream 的高级用法

StreamController

用于手动控制 Stream 的事件发射。

csharp 复制代码
void main() {
  var controller = StreamController<int>();

  controller.stream.listen((data) => print("Data: $data"));

  controller.add(1);
  controller.add(2);
  controller.add(3);

  controller.close();
}

broadcast

将单订阅 Stream 转换为广播 Stream,允许多个监听器同时监听。

scss 复制代码
void main() {
  var controller = StreamController<int>.broadcast();

  controller.stream.listen((data) => print("Listener 1: $data"));
  controller.stream.listen((data) => print("Listener 2: $data"));

  controller.add(1);
  controller.add(2);
  controller.add(3);

  controller.close();
}

StreamTransformer

用于将 Stream 转换为另一个 Stream

javascript 复制代码
void main() {
  var transformer = StreamTransformer<int, String>.fromHandlers(
    handleData: (data, sink) => sink.add("Transformed: $data"),
    handleError: (error, stackTrace, sink) => sink.addError("Error: $error"),
    handleDone: (sink) => sink.close(),
  );

  countStream(5)
      .transform(transformer)
      .listen((data) => print(data));
}

2.4 Stream 在 Flutter 中的应用

StreamBuilder

StreamBuilder 是 Flutter 中用于根据 Stream 更新 UI 的组件。

scala 复制代码
import 'package:flutter/material.dart';

class StreamBuilderExample extends StatelessWidget {
  final Stream<int> stream = Stream.periodic(Duration(seconds: 1), (value) => value).take(10);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("StreamBuilder Example")),
      body: Center(
        child: StreamBuilder<int>(
          stream: stream,
          builder: (context, snapshot) {
            if (snapshot.hasError) {
              return Text("Error: ${snapshot.error}");
            }
            if (!snapshot.hasData) {
              return CircularProgressIndicator();
            }
            return Text("Data: ${snapshot.data}");
          },
        ),
      ),
    );
  }
}

3. Future 和 Stream 的结合

FutureStream 可以结合使用,例如将 Future 的结果作为 Stream 的事件发射。

示例

dart 复制代码
void main() {
  Stream<int> stream = Stream.fromFuture(Future.delayed(Duration(seconds: 2), () => 42));

  stream.listen((data) => print("Data: $data"));
}

总结

  • Future:用于处理单个异步操作的结果,适合处理一次性事件。
  • Stream:用于处理一系列异步事件,适合处理连续的数据流。
  • FutureStream 的结合:可以灵活处理复杂的异步场景。
相关推荐
SuperEugene4 分钟前
Vue Router 实战规范:path/name/meta 配置 + 动态 / 嵌套路由,统一团队标准|状态管理与路由规范篇
开发语言·前端·javascript·vue.js·前端框架
小彭努力中22 分钟前
194.Vue3 + OpenLayers 实战:动态位置 + 高度 + 角度,模拟卫星地面覆盖范围
前端·css·vue.js·openlayers·animate
颜正义23 分钟前
作为前端你还不会 Playwright 进行单元测试吗?
前端·测试
孟陬24 分钟前
国外技术周刊 #3:“最差程序员”带动高效团队、不写代码的创业导师如何毁掉创新…
前端·后端·设计模式
张一凡9325 分钟前
easy-model -- "小而美"的React状态管理方案
前端·javascript·react.js
前端Hardy26 分钟前
纯 HTML/CSS/JS 实现的高颜值登录页,还会眨眼睛!少女心爆棚!
前端·javascript·vue.js
猪八宅百炼成仙1 小时前
解决Vue项目中scrollIntoView导致的布局异常问题
前端
miss1 小时前
Vue2 → Vue3 深度对比:8 大核心优化,性能提升 2 倍
前端·vue.js·架构
绝世唐门三哥2 小时前
React---数组浅拷贝之slice的使用
前端·reactjs
傅里叶2 小时前
Flutter开发的app,实现Google 登录
前端·flutter