Flutter中的异步编程

目录

前言

[1. Future 和 async/await](#1. Future 和 async/await)

1.Future

1.什么是Future?

2.Flutter的三种状态

1.未完成(Uncompleted)

1.定义

2.处理未完成的Future

[2.已完成(Completed with a value)](#2.已完成(Completed with a value))

1.概念

2.处理已完成的Future

3.使用async/await

4.Future的已完成状态的处理

[3.已失败(Completed with an error)](#3.已失败(Completed with an error))

1.概念

2.处理失败状态的Future

[3.使用 async/await](#3.使用 async/await)

4.捕获和处理错误的最佳实践

1.始终捕获错误

2.提供用户反馈

3.记录错误

3.创建Future

1.使用Future构造函数

2.使用Future.delayed

3.使用Future.value和Future.error

4.处理Future

1.使用then和catchError

2.使用async/await

3.Future的链式调用

[2. Stream](#2. Stream)

1.Stream的两种类型

2.创建Stream

1.使用StreamController

2.使用异步生成器函数

3.监听Stream

4.使用StreamBuilder

5.Stream的高级用法

1.转换Stream

2.合并Stream

6.总结

3.FutureBuilder和StreamBuilder

1.FutureBuilder

2.StreamBuilder


前言

在Flutter应用程序开发中,异步编程是必不可少的。异步编程使应用程序能够在不阻塞主线程的情况下执行耗时操作,如网络请求、文件读取和数据库访问。本文将介绍Flutter中常用的异步编程方法及其最佳实践。

1. Future 和 async/await

1.Future

1.什么是Future?

在Flutter中,Future是一个用于表示异步操作结果的类。它代表一个在将来某个时刻会完成的计算,类似于其他编程语言中的Promise。Future非常适合处理那些需要一些时间才能完成的操作,例如网络请求、文件读取或其他耗时任务。

2.Flutter的三种状态

1.未完成(Uncompleted
1.定义

表示异步操作尚未完成。在未完成状态的时候,Future尚未提供结果,也没有抛出错误。

当你调用一个异步函数时,它会返回一个未完成的 Future。该 Future 正在等待函数的异步操作完成或抛出错误。

以下是一个创建和使用Future的示例,展示Future在未完成状态时的行为:

Dart 复制代码
Future<String> fetchData() {
  // 创建一个延迟两秒后完成的Future
  return Future.delayed(Duration(seconds: 2), () => 'Data fetched');
}

void main() {
  print('Fetching data...');
  
  fetchData().then((result) {
    print(result);
  }).catchError((error) {
    print('Error: $error');
  });

  print('Waiting for data...');
}

控制台输入信息如下:

Fetching data...

Waiting for data...

Data fetched

这里可以看到,在调用fetchData之后和Future完成之前,控制台输出了"Waiting for data..."。这表示在这段时间内,Future处于未完成状态。

2.处理未完成的Future

通常,我们使用then方法来处理Future完成时的结果,使用catchError方法来处理Future失败时的错误。

此外,可以使用await关键字在异步函数中等待Future完成:

Dart 复制代码
Future<void> fetchDataAndPrint() async {
  print('Fetching data...');
  
  try {
    String result = await fetchData();
    print(result);
  } catch (error) {
    print('Error: $error');
  }

  print('Data fetching completed.');
}

void main() {
  fetchDataAndPrint();
}

执行这个代码时,控制台的输出如下:

Fetching data...

Data fetched

Data fetching completed.

2.已完成(Completed with a value)
1.概念

已完成状态表示异步操作成功完成,并返回了结果。

2.处理已完成的Future

当Future进入已完成状态时,我们可以使用then方法来处理成功的结果,使用catchError方法来处理失败的错误。另外,可以使用async/await来简化异步代码。

以下是一个创建和使用Future的示例,展示如何处理已完成状态的Future:

Future<String> fetchData() {

// 创建一个延迟两秒后完成的Future

return Future.delayed(Duration(seconds: 2), () => 'Data fetched');

}

void main() {

print('Fetching data...');

fetchData().then((result) {

// 当Future完成时,处理结果

print(result);

}).catchError((error) {

// 当Future失败时,处理错误

print('Error: $error');

});

print('Waiting for data...');

}

执行这段代码时,控制台的输出如下:

Fetching data...

Waiting for data...

Data fetched

在上面的实例中:

1.fetchData函数返回一个在两秒后完成的Future,并返回字符串"Data fetched"。

2.使用then方法处理Future完成时的结果,输出"Data fetched"。

3.使用catchError方法处理Future失败时的错误(示例中未发生错误)。

3.使用async/await

async/await语法使得处理异步操作更加直观和简洁。以下是同样功能的代码,使用async/await:

Future<void> fetchDataAndPrint() async {

print('Fetching data...');

try {

// 使用await等待Future完成

String result = await fetchData();

print(result);

} catch (error) {

print('Error: $error');

}

print('Data fetching completed.');

}

void main() {

fetchDataAndPrint();

}

执行这个代码时,控制台的输出如下:

Fetching data...

Data fetched

Data fetching completed.

在这个示例中:

1.使用await关键字等待Future完成,并获取其结果。

2.try块用于捕获可能的错误,并在catch块中处理错误。

4.Future的已完成状态的处理

在Flutter的完成状态中,有失败和成功两种情况。

1.对于成功完成的Future,使用then方法处理完成的Future,或者在async函数中是用await。

2.对于失败完成的Future,使用catchError方法处理失败完成的Future,或者在async函数中使用try/catch。

3.已失败(Completed with an error)
1.概念

Future的失败状态表示该异步操作已经完成,但由于某种原因失败,并抛出了一个错误。

2.处理失败状态的Future

当Future进入失败状态时,可以使用catchError方法来处理失败的错误,或者在async/await语法中使用try/catch块来捕获和处理错误。

以下是一个创建和使用Future的示例,展示如何处理失败状态的Future:

Future<String> fetchData() {

// 创建一个延迟两秒后失败的Future

return Future.delayed(Duration(seconds: 2), () => throw 'Failed to fetch data');

}

void main() {

print('Fetching data...');

fetchData().then((result) {

// 当Future成功完成时,处理结果

print(result);

}).catchError((error) {

// 当Future失败时,处理错误

print('Error: $error');

});

print('Waiting for data...');

}

执行这段代码时,控制台的输出如下:

Fetching data...

Waiting for data...

Error: Failed to fetch data

在上面这个示例中:

  1. fetchData函数返回一个在两秒后失败的Future,并抛出字符串"Failed to fetch data"。

  2. 使用then方法处理Future成功完成时的结果(在此示例中不会被调用)。

  3. 使用catchError方法处理Future失败时的错误,输出"Error: Failed to fetch data"。

3.使用 async/await

async/await语法使得处理异步操作的失败更加直观和简洁。以下是同样功能的代码,使用async/await:

Future<void> fetchDataAndPrint() async {

print('Fetching data...');

try {

// 使用await等待Future完成

String result = await fetchData();

print(result);

} catch (error) {

// 捕获并处理错误

print('Error: $error');

}

print('Data fetching completed.');

}

void main() {

fetchDataAndPrint();

}

执行这个代码时,控制台的输出如下:

Fetching data...

Error: Failed to fetch data

Data fetching completed.

在这个示例中:

  1. 使用await关键字等待Future完成,并获取其结果。

  2. try块用于捕获可能的错误,并在catch块中处理错误。

4.捕获和处理错误的最佳实践

理解和处理Future的失败状态是Flutter异步编程的重要部分。通过使用catchError和try/catch语法,可以有效地捕获和处理异步操作中的错误,从而编写更健壮和稳定的Flutter应用程序。

1.始终捕获错误

无论使用then和catchError还是async/await,始终确保捕获并处理Future中的错误,以避免未处理的异常。

2.提供用户反馈

在用户界面上提供适当的反馈,如显示错误消息或提示用户重试操作。

3.记录错误

在开发和调试过程中,记录错误信息有助于查找和修复问题。

3.创建Future

创建一个Future非常简单,可以使用Future构造函数、Future.delayed、Future.value和Future.error等方法。

1.使用Future构造函数
Dart 复制代码
Future<String> fetchData() {
  return Future(() {
    return 'Data fetched';
  });
}

void main() {
  fetchData().then((data) {
    print(data);
  });
}
2.使用Future.delayed
Dart 复制代码
Future<String> fetchData() {
  return Future.delayed(Duration(seconds: 2), () {
    return 'Data fetched';
  });
}

void main() {
  fetchData().then((data) {
    print(data);
  });
}
3.使用Future.value和Future.error
Dart 复制代码
void main() {
  Future.value('Immediate data').then((data) {
    print(data);
  });

  Future.error('An error occurred').catchError((error) {
    print(error);
  });
}

4.处理Future

处理Future通常有两种方式:使用then和catchError方法,或者使用async/await语法。

1.使用then和catchError
Dart 复制代码
Future<String> fetchData() {
  return Future.delayed(Duration(seconds: 2), () {
    return 'Data fetched';
  });
}

void main() {
  fetchData().then((data) {
    print(data);
  }).catchError((error) {
    print('Error: $error');
  });
}
2.使用async/await
Dart 复制代码
Future<String> fetchData() async {
  await Future.delayed(Duration(seconds: 2));
  return 'Data fetched';
}

void main() async {
  try {
    String data = await fetchData();
    print(data);
  } catch (error) {
    print('Error: $error');
  }
}
3.Future的链式调用

多个Future可以通过链式调用连接在一起,从而依次处理多个异步操作。

Future<void> fetchData() {

return Future.delayed(Duration(seconds: 2), () {

print('Step 1: Data fetched');

}).then((_) {

return Future.delayed(Duration(seconds: 1), () {

print('Step 2: Additional data fetched');

});

}).then((_) {

print('Step 3: All steps completed');

});

}

void main() {

fetchData();

}

2. Stream

在Flutter中,Stream用于表示一系列异步数据事件。与Future不同,Stream可以提供多个值而不是单个值。它非常适合处理诸如用户输入、实时数据更新和传感器数据等连续数据流。本文将详细介绍Flutter中`Stream`的基本概念和用法。

1.Stream的两种类型

1.单订阅Stream(Single-subscription Stream):只能被单个监听器(listener)监听。这种Stream适用于大多数情况。

2.广播Stream(Broadcast Stream):可以被多个监听器同时监听。这种Stream适用于需要多个订阅者的情况,如事件总线(Event Bus)。

2.创建Stream

创建Stream有多种方式,常见的包括使用StreamController和异步生成器函数。

1.使用StreamController

StreamController用于创建和管理Stream。

Dart 复制代码
void main() {
  // 创建一个StreamController
  final controller = StreamController<int>();

  // 获取Stream
  final stream = controller.stream;

  // 监听Stream
  stream.listen((data) {
    print('Data: $data');
  }, onDone: () {
    print('Stream closed');
  });

  // 添加数据到Stream
  controller.add(1);
  controller.add(2);
  controller.add(3);

  // 关闭Stream
  controller.close();
}

执行这个代码时,控制台的输出如下:

Data: 1

Data: 2

Data: 3

Stream closed

2.使用异步生成器函数

可以使用async*和yield关键字创建Stream。

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

void main() {
  final stream = countStream(5);

  stream.listen((data) {
    print('Data: $data');
  }, onDone: () {
    print('Stream closed');
  });
}

执行这个代码时,控制台的输出如下:

Data: 1

Data: 2

Data: 3

Data: 4

Data: 5

Stream closed

3.监听Stream

使用listen方法可以监听Stream,并处理接收到的数据和错误。

Dart 复制代码
stream.listen(
  (data) {
    print('Data: $data');
  },
  onError: (error) {
    print('Error: $error');
  },
  onDone: () {
    print('Stream closed');
  },
  cancelOnError: false, // 当为true时,接收到错误后将取消订阅
);

4.使用StreamBuilder

在Flutter中,StreamBuilder小部件用于根据Stream构建UI。它会监听Stream并在每次接收到数据时重新构建UI。

Stream<int> countStream(int to) async* {

for (int i = 1; i <= to; i++) {

await Future.delayed(Duration(seconds: 1));

yield i;

}

}

class MyHomePage extends StatelessWidget {

@override

Widget build(BuildContext context) {

return Scaffold(

appBar: AppBar(

title: Text('StreamBuilder Example'),

),

body: Center(

child: StreamBuilder<int>(

stream: countStream(5),

builder: (context, snapshot) {

if (snapshot.connectionState == ConnectionState.waiting) {

return CircularProgressIndicator();

} else if (snapshot.hasError) {

return Text('Error: ${snapshot.error}');

} else if (!snapshot.hasData) {

return Text('No Data');

} else {

return Text('Count: ${snapshot.data}');

}

},

),

),

);

}

}

void main() => runApp(MaterialApp(home: MyHomePage()));

在这个示例中,StreamBuilder监听countStream,并根据Stream的状态更新UI。

5.Stream的高级用法

1.转换Stream

使用Stream的转换方法可以方便地处理数据,例如使用map方法转换数据:

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

void main() {
  final stream = countStream(5).map((data) => data * 2);

  stream.listen((data) {
    print('Transformed Data: $data');
  });
}

2.合并Stream

可以使用StreamGroup来合并多个Stream:

Dart 复制代码
void main() {
  final stream1 = Stream.fromIterable([1, 2, 3]);
  final stream2 = Stream.fromIterable([4, 5, 6]);

  final mergedStream = StreamGroup.merge([stream1, stream2]);

  mergedStream.listen((data) {
    print('Merged Data: $data');
  });
}

6.总结

Stream在Flutter中是处理异步数据事件的强大工具。通过理解和使用StreamController、异步生成器函数以及StreamBuilder,可以有效地处理和显示连续的数据流。希望本文能帮助你更好地理解和使用Flutter中的Stream。

Future 表示不会立即完成的计算。普通函数会返回结果,而异步函数则会返回 Future,后者最终会包含结果。Future 会告诉您结果何时准备就绪。

Stream(流)是一系列异步事件。它类似于异步 Iterable --- 流不会在您请求时获取下一个事件,而是在事件准备就绪时告诉您有事件发生。

3.FutureBuilder和StreamBuilder

FutureBuilder和StreamBuilder是Flutter中用于处理异步数据的两个常用小部件。

1.FutureBuilder

FutureBuilder用于构建依赖于Future的widget。

Dart 复制代码
FutureBuilder<String>(
  future: fetchUserOrder(),
  builder: (context, snapshot) {
    if (snapshot.connectionState == ConnectionState.waiting) {
      return CircularProgressIndicator();
    } else if (snapshot.hasError) {
      return Text('Error: ${snapshot.error}');
    } else {
      return Text('Order: ${snapshot.data}');
    }
  },
);

2.StreamBuilder

StreamBuilder用于构建依赖于Stream的widget。

Dart 复制代码
StreamBuilder<int>(
  stream: countStream(5),
  builder: (context, snapshot) {
    if (snapshot.connectionState == ConnectionState.waiting) {
      return CircularProgressIndicator();
    } else if (snapshot.hasError) {
      return Text('Error: ${snapshot.error}');
    } else if (!snapshot.hasData) {
      return Text('No Data');
    } else {
      return Text('Count: ${snapshot.data}');
    }
  },
);
相关推荐
江上清风山间明月16 小时前
Flutter开发的应用页面非常多时如何高效管理路由
android·flutter·路由·页面管理·routes·ongenerateroute
Zsnoin能1 天前
flutter国际化、主题配置、视频播放器UI、扫码功能、水波纹问题
flutter
早起的年轻人1 天前
Flutter CupertinoNavigationBar iOS 风格导航栏的组件
flutter·ios
HappyAcmen1 天前
关于Flutter前端面试题及其答案解析
前端·flutter
coooliang2 天前
Flutter 中的单例模式
javascript·flutter·单例模式
coooliang2 天前
Flutter项目中设置安卓启动页
android·flutter
JIngles1232 天前
flutter将utf-8编码的字节序列转换为中英文字符串
java·javascript·flutter
B.-2 天前
在 Flutter 中实现文件读写
开发语言·学习·flutter·android studio·xcode
freflying11192 天前
使用jenkins构建Android+Flutter项目依赖自动升级带来兼容性问题及Jenkins构建速度慢问题解决
android·flutter·jenkins
机器瓦力2 天前
Flutter应用开发:对象存储管理图片
flutter