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 的结合:可以灵活处理复杂的异步场景。
相关推荐
Mintopia22 分钟前
像素的进化史诗:计算机图形学与屏幕的千年之恋
前端·javascript·计算机图形学
Mintopia25 分钟前
Three.js 中三角形到四边形的顶点变换:一场几何的华丽变身
前端·javascript·three.js
归于尽39 分钟前
async/await 从入门到精通,解锁异步编程的优雅密码
前端·javascript
陈随易40 分钟前
Kimi k2不行?一个小技巧,大幅提高一次成型的概率
前端·后端·程序员
猩猩程序员1 小时前
Rust 动态类型与类型反射详解
前端
杨进军1 小时前
React 实现节点删除
前端·react.js·前端框架
yanlele1 小时前
【实践篇】【01】我用做了一个插件, 点击复制, 获取当前文章为 Markdown 文档
前端·javascript·浏览器
爱编程的喵1 小时前
React useContext 深度解析:告别组件间通信的噩梦
前端·react.js
望获linux2 小时前
【实时Linux实战系列】多核同步与锁相(Clock Sync)技术
linux·前端·javascript·chrome·操作系统·嵌入式软件·软件