一架梯子,一头程序猿,仰望星空!

Dart 异步编程async/await和Future


Dart是单线程模型的语言,Dart原生通过Future、async和await支持异步编程模型,用法很简单,就是需要理解下为什么需要异步处理和Future的含义。

1.为什么需要异步处理?

前面提到Dart是单线程的,如果我们的程序存在耗时的操作,例如,请求api接口、文件IO等等,这些耗时操作都会阻塞线程,别的事情也干不了。如果在Flutter应用中阻塞线程就会出现不能绘制UI、点击事件没有响应、卡顿之类的情况。异步处理不会阻塞线程,其他任务可以继续运行。

2.Dart异步处理的用法

在Dart语言中有很多库的函数返回 Future 或者 Stream 对象,这些对象都是Dart对异步编程支持的实现。

  • Future - 代表一个异步计算任务,可以获取任务的计算结果。
  • Stream - 代表一个异步的数据序列,通常用于读取连续的数据或者事件。

2.1. Future

前面提到Future代表的是一个异步的计算任务,如果任务还没执行完成,我们是拿不到异步任务的结果。

例子:
我们先看一个http请求的例子

// 导入第三方http库
import 'package:http/http.dart' as http;

main() {
  // 我们要请求的url
  var url = "https://www.tizi365.com/";

  // 调用get函数请求url, 返回一个封装了http请求任务的future对象
  var fTask = http.get(url);
  // 打印future对象, 看看返回什么东西
  print(fTask);

  // 向future对象注册回调函数,处理请求结果
  fTask.then((response) {
    // 异步计算任务完成,这里处理http请求结果。
    // 打印http请求状态码
	print('Response status: ${response.statusCode}');
  });

  // 打印main函数结束标记
  print("main func end.");
}

http.get封装了一个请求url的异步任务,然后返回Future代表这个异步任务,这个时候任务还没执行,所以也拿不到结果。

执行程序输出如下

Instance of 'Future<Response>'
main func end.
Response status: 200

首先打印fTask,输出表示fTask是一个Future对象,将来会返回一个叫Response的结果对象。

重点来了,大家发现是先打印 main func end. ,而不是先输出http的请求状态码。

这是因为Dart在 main函数执行结束的时候才开始处理异步计算任务

异步任务计算完成,才回调我们注册的回调函数,然后打印输出http请求状态码,所以最后才看到http请求的输出。

提示:如果要执行例子程序,需要下载http包,包的版本号: ^0.12.0+2 , 如何导入第三方包,请参考前面的教程。

2.2. await和async

前面例子在处理异步任务的时候,需要注册回调函数,才能处理异步任务,计算的结果,这种通过回调函数书写代码的方式可能性比较差,尤其是多层回调函数层层嵌套的时候。为了解决这个问题,Dart提供了await和async机制,让我们的异步任务代码,书写的时候看起来跟同步代码一样。

接上面http请求的例子:

import 'package:http/http.dart' as http;

// 使用 async关键词,标记main函数是一个异步函数,在函数体之前标记即可。
main() async {
  // 我们要请求的url
  var url = "https://www.tizi365.com/";

  // 请求url, 通过await,等待future异步计算任务的结果,执行成功就直接返回结果
  var response = await http.get(url);

  // 打印http请求状态码
  print('Response status: ${response.statusCode}');

  print("main func end.");
}

执行程序输出:

Response status: 200
main func end.

输出结果的顺序,跟我们书写代码的顺序一致。

通过标记async和await关键词,我们的异步代码,看起来跟同步代码没什么区别。

  • async关键词的作用就是标记一个函数是异步函数。
  • await关键词的作用是等待异步任务的结果。

注意:await关键词只能在标记了async的异步函数中使用,否则报错。

2.2. Stream

Stream代表一个异步的数据序列,是一种异步读取流式数据的方式,例如我们要读取一个文件。

使用格式:

await for (数据类型 变量 in stream类型变量) {
  // 处理数据
}

我们使用await标记for in循环语句,循环读取stream类型的变量中的数据,代码书写也很直观,跟同步代码的书写方式一致。