Flutter中的异步编程

发布于:2024-08-13 ⋅ 阅读:(161) ⋅ 点赞:(0)

目录

前言

1. Future 和 async/await

1.Future

1.什么是Future?

2.Flutter的三种状态

1.未完成(Uncompleted) 

1.定义      

2.处理未完成的Future

2.已完成(Completed with a value)

1.概念

2.处理已完成的Future

3.使用async/await

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

3.已失败(Completed with an error)

1.概念

2.处理失败状态的Future

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

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在未完成状态时的行为:

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完成:

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构造函数
Future<String> fetchData() {
  return Future(() {
    return 'Data fetched';
  });
}

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

void main() {
  fetchData().then((data) {
    print(data);
  });
}
3.使用Future.value和Future.error
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
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
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。

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。

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,并处理接收到的数据和错误。

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方法转换数据:

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:

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。

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。

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}');
    }
  },
);

网站公告

今日签到

点亮在社区的每一天
去签到