▲Dart-‘dart:async’库

Posted itzyjr

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了▲Dart-‘dart:async’库相关的知识,希望对你有一定的参考价值。


dart:async - 异步编程
异步编程通常使用回调方法来实现,但是 Dart 提供了其他方案: FutureStream 对象。 Future 类似与 javascript 中的 Promise,代表在将来某个时刻会返回一个结果。 Stream 类可以用来获取一系列的值,比如,一系列事件。

备忘:
你并不总是需要直接使用 Future 或 Stream 的 API。 Dart 语言支持使用关键字(例如,async 和 await )来实现异步编程。

dart:async 库可以工作在 web 应用及 command-line 应用。

从 Dart 2.1 开始,使用 Future 和 Stream 不需要导入 dart:async ,因为 dart:core 库 export 了这些类。

1. Future

在 Dart 库中随处可见 Future 对象,通常异步函数返回的对象就是一个 Future。当一个 future 完成执行后,future 中的值就已经可以使用了。

  1. 使用await:
    在直接使用 Future API 前,首先应该考虑 await 来替代。代码中使用 await 表达式会比直接使用 Future API 更容易理解。

下面代码使用 Future 的 then() 方法在同一行执行了三个异步函数,要等待上一个执行完成,再执行下一个任务。

void runUsingFuture() 
  // ...
	findEntryPoint().then((entryPoint) 
		return runExecutable(entryPoint, args);
	).then(flushThenExit);

通过 await 表达式实现等价的代码,看起来非常像同步代码:

Future<void> runUsingAsyncAwait() async 
	// ...
	var entryPoint = await findEntryPoint();
	var exitCode = await runExecutable(entryPoint, args);
	await flushThenExit(exitCode);

重要说明:
async 函数返回 Future 对象。如果你不希望你的函数返回一个 future 对象,可以使用其他方案。例如,你可以在你的方法中调用一个 async 方法。

  1. 基本用法:
    当 future 执行完成后,then() 中的代码会被执行。 then() 中的代码会在 future 完成后被执行。例如,HttpRequest.getString() 返回一个 future 对象,因为 HTTP 请求可能需要一段时间。当 Future 完成并且保证字符串值有效后,使用 then() 来执行你需要的代码,使用 catchError() 来处理一些 Future 对象可能抛出的错误或者异常:
HttpRequest.getString(url).then((String result) 
	print(result);
).catchError((e) 
	// Handle or ignore the error.
);

then().catchError() 组合是 try-catch 的异步版本。

重要说明:
确保调用 catchError() 方式在 then() 的结果上,而不是在原来的 Future 对象上调用。否则的话,catchError() 就只能处理原来 Future 对象抛出的异常,而无法处理 then() 代码里面的异常。

  1. 链式异步编程:
    then() 方法返回一个 Future 对象,这样就提供了一个非常好的方式让多个异步方法按顺序依次执行。如果用 then() 注册的回调返回一个 Future ,那么 then() 返回一个等价的 Future 。如果回调返回任何其他类型的值,那么 then() 会创建一个以该值完成的新 Future 。
    下面示例中,方法按下面顺序执行:
    1.costlyQuery() 2.expensiveWork() 3.lengthyComputation()
Future result = costlyQuery(url);
result.then((value) => expensiveWork(value))
      .then((_) => lengthyComputation())
      .then((_) => print('Done!'))
      .catchError((exception) 
	  	  /* Handle exception... */
	  );

使用 await 编写的等效代码:

try 
  final value = await costlyQuery(url);
  await expensiveWork(value);
  await lengthyComputation();
  print('Done!');
 catch (e) 
  /* Handle exception... */

  1. 等待多个Future:
    有时代码逻辑需要调用多个异步函数,并等待它们全部完成后再继续执行。使用 Future.wait() 静态方法管理多个 Future 以及等待它们完成:
Future<void> deleteLotsOfFiles() async =>  ...
Future<void> copyLotsOfFiles() async =>  ...
Future<void> checksumLotsOfOtherFiles() async =>  ...
await Future.wait([
  deleteLotsOfFiles(),
  copyLotsOfFiles(),
  checksumLotsOfOtherFiles(),
]);
print('Done with all the long steps!');

参考API:dart:async libraryAsynchrony supportFuture

2. Stream

在 Dart API 中 Stream 对象随处可见,Stream 用来表示一系列数据。例如,html 中的按钮点击就是通过 stream 传递的。同样也可以将文件作为数据流来读取。

  1. 异步循环:
    有时,可以使用异步 for 循环 await for ,来替代 Stream API 。

Stream的listen()注册回调函数,该函数在每次在有更多数据可用、发生错误或流完成时执行。进一步的功能在stream上提供,通过调用stream来实现。

Stream<List<int>> stream = File('quotes.txt').openRead();
stream.transform(utf8.decoder).forEach(print);

下面示例函数,使用 Stream 的 listen() 方法来订阅文件列表,传入一个搜索文件或目录的函数,通过listen()方法获取实际数据:

void main(List<String> arguments) 
  // ...
  FileSystemEntity.isDirectory(searchPath).then((isDir) 
    if (isDir) 
      final startingDir = Directory(searchPath);
      startingDir.list().listen((entity) 
        if (entity is File) 
            searchFile(entity, searchTerms);
        
      );
     else 
        searchFile(File(searchPath), searchTerms);
    
  );

下面是使用 await 表达式和异步 for 循环 (await for) 实现的等价的代码,看起来更像是同步代码:

Future<void> main(List<String> arguments) async 
  // ...
  if (await FileSystemEntity.isDirectory(searchPath)) 
    final startingDir = Directory(searchPath);
    await for (final entity in startingDir.list()) 
      if (entity is File) 
          searchFile(entity, searchTerms);
      
    
   else 
      searchFile(File(searchPath), searchTerms);
  

重要说明:
在使用 await for 前,确认这样能保持代码清晰,并希望获取所有 stream 的结果。例如,你通常并不会使用 await for 来监听 DOM 事件,因为 DOM 会发送无尽的流事件。如果在同一行使用 await for 注册两个 DOM 事件,那么第二个事件永远不会被处理。

  1. 监听流数据(stream data):
    使用 await for 或者使用 listen() 方法监听 stream,来获取每个到达的数据流值:
// Add an event handler to a button.
submitButton.onClick.listen((e) 
	// When the button is clicked, it runs this code.
	submitData();
);

下面示例中,ID 为 “submitInfo” button 提供的 onClick 属性是一个 Stream 对象。

querySelector('#submitInfo')!.onClick.forEach((_) => print('Click.'));

如果只关心其中一个事件,可以使用,例如,firstlast,或 single 属性来获取。要在处理时间前对事件进行测试,可以使用,例如 firstWhere()lastWhere(),或 singleWhere() 方法。
如果只关心事件中的一个子集,可以使用,例如,skip()skipWhile()take()takeWhile(),和 where()

Iterable<int> iterable = [1,2,3];
iterable.first;
iterable.last;

Iterable<int> iterable = -1,0,1,2;
//找到返回,找不到返回orElse的值。orElse是可选参数。
int negtive = iterable.firstWhere((item)=> item < 0,orElse: ()=>99);
print(negtive);// -1
  1. 传递流数据(stream data):
    常常,在使用流数据前需要改变数据的格式。使用 transform() 方法生成具有不同类型数据的流:
var lines = inputStream
           .transform(utf8.decoder)
           .transform(const LineSplitter());

上面例子中使用了两个 transformer 。第一个使用 utf8.decoder 将整型流转换为字符串流。接着,使用了 LineSplitter 将字符串流转换为多行字符串流。这些 transformer 来自 dart:convert 库。

  1. 处理错误和完成:
    处理错误和完成代码方式,取决于使用的是异步 for 循环(await for)还是 Stream API 。
    a. 如果使用的是异步 for 循环,那么通过 try-catch 来处理错误。代码位于异步 for 循环之后,会在 stream 被关闭后执行。
Future<void> readFileAwaitFor() async 
  var config = File('config.txt');
  Stream<List<int>> inputStream = config.openRead();
  var lines = inputStream.transform(utf8.decoder).transform(const LineSplitter());
  try 
	  await for (final line in lines) 
      	print('Got $line.length characters from stream');
    
    print('file is now closed');
   catch (e) 
    print(e);
  

b. 如果使用的是 Stream API,那么通过注册 onError 监听来处理错误。如果代码位于注册的 onDone 中,则会在 stream 被关闭后执行。

var config = File('config.txt');
Stream<List<int>> inputStream = config.openRead();
inputStream.transform(utf8.decoder).transform(const LineSplitter())
    .listen((String line) 
  		print('Got $line.length characters from stream');
	, onDone: () 
  		print('file is now closed');
	, onError: (e) 
  		print(e);
	);

参考API:Stream

3. 更多内容

dart:io概览
Asynchronous programming: futures, async, await
Futures and error handling
Asynchronous programming: streams
Creating streams in Dart
Dart asynchronous programming: Isolates and event loops

以上是关于▲Dart-‘dart:async’库的主要内容,如果未能解决你的问题,请参考以下文章

Dart async proc

flutter http get请求

摇一摇检测

在 Dart 中,如何确保流更新在继续之前完成?

dart子进程无权访问终端

Flutter 读写本地文件