简介
通常,应用程序需要与外部API和数据库交互的能力。随之而来的问题是,在等待某些请求和操作完成时,处理以不同于编写顺序的顺序运行的代码。
在本文中,您将探索Dart,特别是Flutter如何处理异步请求。
前提条件
要完成本教程,您需要:
- 下载并安装Flutter.
- 下载并安装Android StudioorVisual Studio代码.
- 建议为您的代码编辑器安装插件:
-Android Studio安装的
Flutter
和Dart
插件。 -为Visual Studio代码安装的Flutter
扩展。
本教程已通过Flutter v2.0.6、Android SDK v31.0.2和Android Studio v4.1验证。
了解异步码
使用同步代码时,当我们向外部API发送信息请求时,我们将需要一段时间才能得到响应。我们的机器将等待它完成,暂停可能与初始请求无关的事情。问题是,我们不希望脚本在每次需要一些时间时都停止运行,但我们也不希望它过早地运行任何依赖于返回数据的东西,这可能会导致错误,尽管请求成功。
两全其美的做法是设置我们的逻辑,允许我们的机器在等待请求返回的同时提前工作,同时只允许依赖于该请求的代码在它可用时运行。
我们的应用程序的数据很可能是四种形式中的一种,这取决于它们是否已经可用,以及它们是否是单一的。
此示例将探讨期货和流。
设置项目
将环境设置为颤动后,可以运行以下命令来创建新应用程序:
1flutter create flutter_futures_example
导航到新项目目录:
1cd flutter_futures_example
使用fltter create
将生成一个演示应用程序,其中将显示按钮被点击的次数。
此示例的一部分依赖于REST国家/地区API.如果您提供国家/地区名称,此接口将返回国家/地区信息。例如,Canada
的请求如下:
1https://restcountries.eu/rest/v2/name/Canada
这还需要http
package。
在代码编辑器中打开pubspec.yaml
,添加以下插件:
1[label pubspec.yaml]
2dependencies:
3 flutter:
4 sdk: flutter
5
6 http: 0.13.3
现在,在代码编辑器中打开main.dart
并修改以下代码行以显示 Get Country 按钮:
1[label lib/main.dart]
2import 'package:flutter/material.dart';
3import 'package:http/http.dart' as http;
4import 'dart:convert';
5
6void main() {
7 runApp(MyApp());
8}
9
10class MyApp extends StatelessWidget {
11 @override
12 Widget build(BuildContext context) {
13 return MaterialApp(
14 title: 'Flutter Demo',
15 home: MyHomePage(),
16 );
17 }
18}
19
20class MyHomePage extends StatelessWidget {
21 void getCountry() {}
22
23 @override
24 Widget build(BuildContext context) {
25 return Scaffold(
26 body: Center(
27 child: MaterialButton(
28 onPressed: () => getCountry(),
29 child: Container(
30 color: Colors.blue,
31 padding: EdgeInsets.all(15),
32 child: Text('Get Country', style: TextStyle(color: Colors.white))
33 ),
34 ),
35 ),
36 );
37 }
38}
至此,您就有了一个新的带有HTTP
包的Ffltter项目。
使用then
和catchError
非常类似于JavaScript](https://andsky.com/tech/tutorials/js-error-handling-try-catch),DART中的[Try...Catch],它允许我们将方法链接在一起,以便我们可以轻松地将返回数据从一个方法传递到下一个方法,它甚至返回一种类似于承诺的数据类型,称为Futures。期货是任何单一类型的数据,如字符串,稍后将提供。
要使用这种技术,执行您的操作,然后只需将.then
与我们作为参数传入的返回数据链接起来,并按照我们想要的方式使用它。在这一点上,你可以继续链接额外的.then
。对于错误处理,在末尾使用.catchError
并抛出传递给它的任何内容。
使用您的代码编辑器重新访问main.dart
,并使用.then
和.catchError
。首先,将void GetCountry(){}
替换为Future GetCountry(Country)
。然后,在onPressed:()=>GetCountry()
中添加国家名称:
1[label lib/main.dart]
2// ...
3
4class MyHomePage extends StatelessWidget {
5 Future getCountry(country) {
6 Uri countryUrl = Uri.http('restcountries.eu', '/rest/v2/name/$country');
7
8 http
9 .get(countryUrl)
10 .then((response) => jsonDecode(response.body)[0]['name'])
11 .then((decoded) => print(decoded))
12 .catchError((error) => throw(error));
13 }
14
15 @override
16 Widget build(BuildContext context) {
17 return Scaffold(
18 body: Center(
19 child: MaterialButton(
20 onPressed: () => getCountry('Canada'),
21 child: Container(
22 color: Colors.blue,
23 padding: EdgeInsets.all(15),
24 child: Text('Get Country', style: TextStyle(color: Colors.white))
25 ),
26 ),
27 ),
28 );
29 }
30}
保存更改并在模拟器中运行应用程序。然后,点击[获取国家/地区]按钮。您的主机将记录国家/地区的名称。
使用async
和wait
许多人认为可读性很强的另一种语法是Async/AWait。
Async/Await的工作原理与JavaScript完全相同(https://andsky.com/tech/tutorials/js-tutoric-functions),我们在函数名后使用await
关键字,并在需要一些时间运行的任何内容(如get请求)之前添加await
关键字。
使用代码编辑器重新访问main.dart
,并使用async
和wait
。现在,它之后的所有内容都将在返回值时运行。对于错误处理,我们可以在Try/Catch块中抛出错误。
1[label lib/main.dart]
2// ...
3
4class MyHomePage extends StatelessWidget {
5 Future getCountry(country) async {
6 Uri countryUrl = Uri.http('restcountries.eu', '/rest/v2/name/$country');
7
8 try {
9 http.Response response = await http.get(countryUrl);
10 Object decoded = jsonDecode(response.body)[0]['name'];
11 print(decoded);
12 } catch (e) { throw(e); }
13 }
14
15 @override
16 Widget build(BuildContext context) {
17 return Scaffold(
18 body: Center(
19 child: MaterialButton(
20 onPressed: () => getCountry('Canada'),
21 child: Container(
22 color: Colors.blue,
23 padding: EdgeInsets.all(15),
24 child: Text('Get Country', style: TextStyle(color: Colors.white))
25 ),
26 ),
27 ),
28 );
29 }
30}
保存更改并在模拟器中运行应用程序。然后,点击[获取国家/地区]按钮。您的主机将记录国家/地区的名称。
使用Streams
DART的特殊之处在于,当我们有许多值被异步加载时,它使用了流。我们可以让连接保持打开并为新数据做好准备,而不是像我们的GET请求那样打开一次连接。
由于我们的示例通过设置允许流的后端(如使用Firebase或GraphQL))会变得有点太复杂,因此我们将通过每秒发出一条新的‘消息’来模拟聊天应用程序数据库中的更改。
我们可以使用StreamController
类创建Stream,它的工作方式类似于List
,因为它的行为类似于Futures列表。
我们可以通过Stream
上的属性来控制我们的Stream,比如listen
和close
来启动和停止它。
<$>[警告]
警告: 当您的微件被移除时,一定要始终使用Close()
。数据流将持续运行,直到它们被关闭,并将侵蚀计算能力,即使原始窗口小部件消失了。
<$>
现在,在代码编辑器中打开main.dart
,替换以下代码行,导入dart:async
,并使用StatefulWidget
:
1[label lib/main.dart]
2import 'package:flutter/material.dart';
3import 'dart:async';
4
5void main() {
6 runApp(MyApp());
7}
8
9class MyApp extends StatelessWidget {
10 @override
11 Widget build(BuildContext context) {
12 return MaterialApp(
13 title: 'Flutter Demo',
14 home: MyHomePage(),
15 );
16 }
17}
18
19class MyHomePage extends StatefulWidget {
20 @override
21 _MyHomePageState createState() => _MyHomePageState();
22}
23
24class _MyHomePageState extends State<MyHomePage> {
25 StreamController<String> streamController = StreamController();
26
27 void newMessage(int number, String message) {
28 final duration = Duration(seconds: number);
29
30 Timer.periodic(duration, (Timer t) => streamController.add(message));
31 }
32
33 void initState() {
34 super.initState();
35
36 streamController.stream.listen((messages) => print(messages));
37
38 newMessage(1, 'You got a message!');
39 }
40
41 void dispose() {
42 streamController.close();
43
44 super.dispose();
45 }
46
47 @override
48 Widget build(BuildContext context) {
49 return Scaffold(
50 body: Center(
51 child: Container(
52 padding: EdgeInsets.all(15),
53 child: Text('Streams Example'),
54 ),
55 ),
56 );
57 }
58}
该代码将在控制台中连续打印出‘您收到消息`。
标准流可能有点受限,因为它们一次只允许一个侦听器。相反,我们可以使用StreamController
类上的broadcast
属性来打开多个通道。
1[label lib/main.dart]
2// ...
3
4StreamController<String> streamController = StreamController.broadcast();
5
6// ...
7
8void initState() {
9 super.initState();
10
11 streamController.stream.listen((messages) => print('$messages - First'));
12 streamController.stream.listen((messages) => print('$messages - Second'));
13
14 newMessage(1, 'You got a message!');
15}
16
17// ...
该代码将在控制台中连续打印出您有消息-第一
和`您有消息-第二‘。
结论
在本文中,您探索了Dart(特别是Flutter)如何处理异步请求。Dart中的异步编程将允许您开始开发智能和动态应用程序。
如果您想了解更多有关Ffltter的知识,请查看我们的Ffltter主题页面以获取练习和编程项目。