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}');
    }
  },
);
相关推荐
Mingyueyixi2 小时前
Flutter Spacer引发的The ParentDataWidget Expanded(flex: 1) 惨案
前端·flutter
crasowas10 小时前
Flutter问题记录 - 适配Xcode 16和iOS 18
flutter·ios·xcode
老田低代码1 天前
Dart自从引入null check后写Flutter App总有一种难受的感觉
前端·flutter
AiFlutter2 天前
Flutter Web首次加载时添加动画
前端·flutter
ZemanZhang3 天前
Flutter启动无法运行热重载
flutter
AiFlutter4 天前
Flutter-底部选择弹窗(showModalBottomSheet)
flutter
帅次4 天前
Android Studio:驱动高效开发的全方位智能平台
android·ide·flutter·kotlin·gradle·android studio·android jetpack
程序者王大川4 天前
【前端】Flutter vs uni-app:性能对比分析
前端·flutter·uni-app·安卓·全栈·性能分析·原生
yang2952423614 天前
使用 Vue.js 将数据对象的值放入另一个数据对象中
前端·vue.js·flutter
我码玄黄4 天前
解锁定位服务:Flutter应用中的高德地图定位
前端·flutter·dart