Flutter框架之Dart语言——3.库-异步-Stream流-async和await-元数据

  • 工具:要让 Flutter 在你的开发环境中正常使用,依赖于以下的工具:

    • Windows PowerShell 5.0 或者更高的版本(Windows 10 中已预装)

    • Git for Windows 2.x,并且勾选从 Windows 命令提示符使用 Git 选项。

      如果 Windows 版的 Git 已经安装过了,那么请确保能从命令提示符或者 PowerShell 中直接执行 git 命令。

一、库

  • 在Dart中,库的使用通过import关键字引入。
  • library指令可以创建一个库,每个Dart文件都是一个库,即使没有使用library指令来指定。

Dart中的库有三种:

  • 自定义的库

  • 系统内置库

    • import ‘dart:math’;
    • import ‘dart:io’;
    • import ‘dart:convert’;
  • Pub包管理系统中的库(第三方库)

    • https://pub.dev/packages
    • https://pub.flutter-io.cn/packages
    • https://pub.dartlang.org/flutter/

    ​ 1、需要在自己想项目根目录新建一个pubspec.yaml

    ​ 2、在pubspec.yaml文件 然后配置名称 、描述、依赖等信息

    ​ 3、然后运行 pub get 获取包下载到本地

    ​ 4、项目中引入库 import ‘package:http/http.dart’ as http; 看文档使用

1.分类

1)自定义库

import 'lib/xxx.dart';

当一个类的内容过多时,若把这个类与main()主方法或与其他类写在同一个文件中,将会导致这个文件过大而不便于管理,此时我们可以把这个类独立成一个文件,当另一个类需要使用这个独立成文件的类,通过以上的引用格式,就可以使用这个独立类里的方法了。

2)系统内置库

import 'dart:math';

在math库中有许多数学操作方法,例如以下截图中的求最大值和最小值:

min(20, 10);                                   //返回10
max(20, 10);                                   //返回20
import 'dart:io';

3)第三方库

Pub包管理系统中的库(第三方库)
例如从 https://pub.dev/packages 获取http第三方库

  • 从下面网址中找到要用的库(以http库为例):

    https://pub.dev/packages
    
  • 打开项目中的pubspec.yaml文件,找到 dependencies:,在dependencies下面添加内容:

    http: ^0.12.2
    
  • 打开AndroidStudio的命令工作台:运行pub get 命令 ,获取远程库

  • 引用库

2.Dart库冲突

当两个库中有相同名称的标识符,难以辨别使用的标识符属于哪一个库,在Dart语言中使用库的重命名解决。

在当前类中引用了两个库文件,Persion1.dart和Persion2.dart这两个库文件中都有对Perdsion类的定义,当前类中有一个main()方法,这个方法实例化了一个Persion类,但是这个时候回报错,因为IDE不清楚调用的是哪一个库文件里定义的Persion类。

import 'package:flutter_app_demo14/Persion1.dart';
import 'package:flutter_app_demo14/Persion2.dart';

main() {
  Persion p = new Persion();                             //报错,IDE不清楚调用的是哪一个库文件里定义的Persion类
}

**解决办法:**使用 as 关键字给引用到的库重命名:

库名 as XXX
import 'package:flutter_app_demo14/Persion1.dart';
import 'package:flutter_app_demo14/Persion2.dart' as lib;            // as关键字给库重命名为lib

main() {
  Persion p = Persion();                                            //这里Persion使用的是Persion1.dart里的
  lib.Persion p1 = new lib.Persion();                               //这里Persion使用的是Persion2.dart里的
}

3.导入

1)步骤

  • 从下面网址找到要用的库
    https://pub.dev/packages
    https://pub.flutter-io.cn/packages
    https://pub.dartlang.org/flutter/

  • 创建一个pubspec.yaml文件,配置dependencies内容如下

name: name1
description: 项目描述
dependencies:
  http: ^0.12.0+2
  date_format: ^1.0.6
environment:
  sdk: '>=1.19.0 <3.0.0'
  flutter: ^0.1.2
  • 在代码所在文件夹中运行cmd,运行pub get 获取远程库

    PS F:\JavaProject\DartProject> pub get 
    
    
  • 看文档引入库使用

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

main() async {
  var url =
      "http://www.phonegap100.com/appapi.php?a=getPortalList&catid=20&page=1";
  // Await the http get response, then decode the json-formatted responce.
  var response = await http.get(url);
  if (response.statusCode == 200) {
    var jsonResponse = convert.jsonDecode(response.body);
    print(jsonResponse);
  } else {
    print("Request failed with status: ${response.statusCode}.");
  }
  print(formatDate(DateTime(1989, 2, 21), [yyyy, '*', mm, '*', dd]));
}

2)部分导入

如果只需要导入库的一部分,有两种模式:

// 模式一:只导入需要的部分,使用show关键字,如下例子所示:
import 'package:lib1/lib1.dart' show foo;
// 模式二:隐藏不需要的部分,使用hide关键字,如下例子所示:
import 'package:lib2/lib2.dart' hide foo;
// import 'lib/myMath.dart' show getAge;

 import 'lib/myMath.dart' hide getName;

void main(){
//  getName();
  getAge();
}

3)延迟加载

  • 延迟加载也称为懒加载,可以在需要的时候再进行加载。懒加载的最大好处是可以减少APP的启动时间。
  • 懒加载使用deferred as关键字来指定,如下例子所示:
import 'package:deferred/hello.dart' deferred as hello;

// 当需要使用的时候,需要使用loadLibrary()方法来加载:

greet() async {
  await hello.loadLibrary();
  hello.printGreeting();
}

二、异步编程

异步编程是为了处理那些耗时的、可能会阻塞程序执行的操作,使得程序在等待异步函数操作完成的同时可以执行其他任务,提高程序的响应性和性能。

  • Dart与JavaScript一样是单线程模型。Dart异步编程区别于传统的多线程异步方式。
  • Dart代码可以运行在多个isolate上。isolate可以看做一个微小的线程,isolate由虚拟机调度,isolate之间没有共享内存,因此它们之间没有竞争,不需要锁,不用担心死锁。
  • 由于没有共享内存,所以它们之间唯一的通信只能通过Port进行,而且Dart中的消息传递也总是异步的。
  • Dart中两种方式可以使用Future对象来进行异步编程

Dart 是一种支持异步编程的现代化编程语言,它提供了丰富的异步编程工具和语法,使得开发者可以编写高效、响应式的异步代码。在 Dart 中,异步编程主要通过异步函数、Future、async/await 和 Stream 等机制来实现。

1.名词解释

1)并发与并行

  • 并发(concurrency):把任务在不同的时间点交给处理器进行处理。在同一时间点,任务并不会同时运行。
  • 并行(parallelism):把每一个任务分配给每一个处理器独立完成。在同一时间点,任务一定是同时运行。

2)异步函数

异步函数是 Dart 中用于执行异步操作的函数,其定义方式为在函数体前面加上 async 关键字。异步函数可以使用 await 关键字等待其他异步操作的完成,并且异步函数的返回值类型通常是 Future 或者 Stream

Future<void> fetchData() async {
  // 异步操作,例如网络请求、文件读写等
  await Future.delayed(Duration(seconds: 1)); // 模拟延迟1秒
  print('Data fetched!');
}

3)事件循环

Dart中,实际上有两种队列:

  1. 事件队列(event queue),包含所有的外来事件:I/Omouse eventsdrawing eventstimersisolate之间的信息传递。
  2. 微任务队列(microtask queue),表示一个短时间内就会完成的异步任务。它的优先级最高,高于event queue,只要队列中还有任务,就可以一直霸占着事件循环。microtask queue添加的任务主要是由 Dart内部产生。

因为 microtask queue 的优先级高于 event queue ,所以如果 microtask queue有太多的微任务, 那么就可能会霸占住当前的event loop。从而对event queue中的触摸、绘制等外部事件造成阻塞卡顿。

在每一次事件循环中,Dart总是先去第一个microtask queue中查询是否有可执行的任务,如果没有,才会处理后续的event queue的流程。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3.Future异步对象

通常异步函数返回的对象就是一个 Future。 当一个 Future 执行完后它里面的值就可以使用了。Future 是一个泛型类,可以指定类型(如Future<T>表示一个 T 类型的异步操作结果,如果异步操作不需要结果,则类型为 Future<void>),Future对象其实就代表了在事件队列中的一个事件的结果。通过 Future 可以实现在异步操作完成后获取结果、处理异常等操作。

异步任务最多还是优先级更低的 event queueDartevent queue 的任务建立提供了一层封装,就是在Dart中经常用到的Future。正常情况下,一个 Future 异步任务的执行是相对简单的:

  1. 声明一个 Future 时,Dart 会将异步任务的函数执行体放入event queue,然后立即返回,后续的代码继续同步执行。
  2. 当同步执行的代码执行完毕后,event queue会按照加入event queue的顺序(即声明顺序),依次取出事件,最后同步执行 Future 的函数体及后续的操作。

1)处理Future结果

对于Future来说,异步处理成功了就执行成功的操作,异步处理失败了就捕获错误或者停止后续操作。一个Future只会对应一个结果,要么成功,要么失败。

Future的所有API的返回值仍然是一个Future对象,所以可以很方便的进行链式调用

Dart提供了下面三个方法用来处理Future的结果。

Future<R> then<R>(FutureOr<R> onValue(T value), {Function onError});
Future<T> catchError(Function onError, {bool test(Object error)});
Future<T> whenComplete(FutureOr action());
(1)then()

Future.then()用来注册一个Future完成时要调用的回调。如果 Future 有多个then,它们也会按照链接的先后顺序同步执行,同时也会共用一个event loop

then()的返回值同样是一个future对象,可以使用 then() 在 future 完成时执行其他代码,可以利用队列的原理进行组合异步任务,通过 .then 的形式获取 Future 对象执行成功后的结果。

void testFuture() async {
    Future.value(1).then((value) {
      return Future.value(value + 2);
    }).then((value) {
      return Future.value(value + 3);
    }).then(print);
}
testFuture();
  
print("在testFuture()执行之后打印。");
// 在testFuture()执行之后打印。
// 6

同时,then()会在 Future函数体执行完毕后立刻执行:

void testFuture() async {
     var future = new Future.value('a').then((v1) {
      return new Future.value('$v1 b').then((v2) {
        return new Future.value('$v2 c').then((v3) {
          return new Future.value('$v3 d');
        });
      });
    });
    future.then(print, onError: print);
}
testFuture();
  
print("在testFuture()执行之后打印。");
// 在testFuture()执行之后打印。
// a b c d

那么问题来了,如果Future已经执行完毕了,我们再来获取到这个Future的引用,然后再继续调用then()方法。那么此时,Dart会如何处理这种情况?对于这种情况,Dart会将后续加入的then()方法体放入microtask queue,尽快执行:

举例:

import 'dart:io';

void main() {
  	new File("/xxx/xxx/a.txt").readAsString().then((content) {
    	print(content);
    	return "1"; //1被转化为 Future<String>类型返回
  	}).then((i){
    	print(i);
  	}).catchError((e, s) {
   	 	print(s);
 	});
}

onError接收到Future的错误

void main() {   
    testThen().then((res) {     
        // 不执行 onValue 的方式     
        print(res);     
        return "加工后的${res}";   
    }, onError: (err) {     
        // 接收到 Future 的错误     
        print(err); 
        // 这是一个异常   
    }); 
} 
 
Future<String> testThen() {   
    return new Future(() {     
        throw ('这是一个异常');   
    }); 
(2)catchError

它比 then/onError 的接受范围要广,它不仅能接受 Future 中的异常,还能接受 then 中的异常

void testFuture() async {
  new Future.error('Future 发生错误啦!').catchError(print, test: (error) {
    return error is String;
  });
}
 
testFuture();
 
print("在testFuture()执行之后打印。");
// 在testFuture()执行之后打印。
// Future 发生错误啦!

Future.catchError回调只处理原始Future抛出的错误,不能处理回调函数抛出的错误,onError只能处理当前Future的错误

void testFuture() async {
    new Future.error('Future 发生错误啦!').then((_) {
       throw 'new error';
    }).catchError((error) {
      print('error: $error');
      throw 'new error2';
    }).then(print, onError:(error) {
      print("handle new error: $error");
    });
}
testFuture();
  
print("在testFuture()执行之后打印。");
// 在testFuture()执行之后打印。
// error: Future 发生错误啦!
// handle new error: new error2
void main() {   
    testThen().then((res) {       
        print(res);       
        return "加工后的${res}";     
    },onError: (err) {       
        print("onError: ${err}");       
        throw ("这是 onError  抛出的异常");     
    }).catchError((err) {       
        print("catchError: ${err}");     
    }); 
} 
 
Future<String> testThen() {   
    return new Future(() {     
        throw ('这是一个异常');   
    }); 
} 
// 执行结果 
// onError: 这是一个异常 
// catchError: 这是 onError  抛出的异常
(3)whenComplete

在 Future 完成之后总是会调用,不管是错误导致的完成还是正常执行完毕,并且返回一个 Future 对象。

Future.whenComplete 在Future完成之后总是会调用,不管是错误导致的完成还是正常执行完毕。比如在网络请求前弹出加载对话框,在请求结束后关闭对话框。并且返回一个Future对象:

void testFuture() async {
    var random = new Random();
    new Future.delayed(new Duration(seconds: 1), () {
        if (random.nextBool()) {
          return 'Future 正常';
        } else {
          throw 'Future 发生错误啦!';
        }
    }).then(print).catchError(print).whenComplete(() {
        print('Future whenComplete!');
    });
}
testFuture();
  
print("在testFuture()执行之后打印。");
// 在testFuture()执行之后打印。
// Future 发生错误啦!
// Future whenComplete!

// 在testFuture()执行之后打印。
// Future 正常
// Future whenComplete!
void main() {   
    testThen().then((res) {     
        print(res);     
        return "加工后的${res}";   
    }, onError: (err) {     
        print("onError: ${err}");     
        throw ("这是 onError  抛出的异常");   
    }).catchError((err) {     
        print("catchError: ${err}");   
    }).whenComplete(() {     
        print("执行完成");   
    }); 
} 
 
Future<String> testThen() {   
    return new Future(() {     
        throw ('这是一个异常');   
    }); 
} 
// 执行结果 
// onError: 这是一个异常 
// catchError: 这是 onError  抛出的异常 
// 执行完成

3)常用属性

(1)timeout()

设置异步操作的超时时长,返回的同样是个 Future

import 'dart:async';
 
void main() {
  futureA();
}
 
Future futureA() {
  return Future(() {
    new Timer(Duration(seconds: 5), () {
      print("测试");
    });
  }).timeout(Duration(seconds: 3)).then((res) {
    // 设置超时
    print(res);
    print("超时后执行的 then");
  });
}
// 执行结果
// null
// 超时后执行的 then
// 测试

本来Future会在2s后完成,但是timeout声明的是1s后超时,所以1s后Future会抛出TimeoutException

void testFuture() async {
    new Future.delayed(new Duration(seconds: 2), () {
      return 1;
    }).timeout(new Duration(seconds:1)).then(print).catchError(print);
}
testFuture();
  
print("在testFuture()执行之后打印。");
// 在testFuture()执行之后打印。
// TimeoutException after 0:00:01.000000: Future not completed
(2)foreach()

Future.foreach根据某个集合对象,创建一系列的Future。并且会按顺序执行这些Future。例如,根据{1,2,3}创建3个延迟对应秒数的Future。执行结果为1秒后打印1,再过2秒打印2,再过3秒打印3,总时间为6秒:

void testFuture() async {
    Future.forEach({1,2,3}, (num){
      return Future.delayed(Duration(seconds: num),(){print("第$num秒执行");});
    });
}
testFuture();
  
print("在testFuture()执行之后打印。");
// 在testFuture()执行之后打印。
// 第1秒执行
// 第2秒执行
// 第3秒执行
(3)wait()

等待多个Future完成,并收集它们的结果。有两种情况:

  • 所有Future都有正常结果返回。则Future的返回结果是所有指定Future的结果的集合:
void testFuture() async {
    var future1 = new Future.delayed(new Duration(seconds: 1), () => 1);
    var future2 = new Future.delayed(new Duration(seconds: 2), ()  => 2);
 	var future3 = new Future.delayed(new Duration(seconds: 3), () => 3);
  Future.wait({future1,future2,future3}).then(print).catchError(print);
}
 
testFuture();
print("在testFuture()执行之后打印。");
// 在testFuture()执行之后打印。
// [1, 2, 3]
  • 其中一个或者几个Future发生错误,产生了error。则Future的返回结果是第一个发生错误的Future的值:
void testFuture() async {
    var future1 = new Future.delayed(new Duration(seconds: 1), () => 1);
    var future2 =
    new Future.delayed(new Duration(seconds: 2), () {
      throw 'Future 发生错误啦!';
    });
    var future3 = new Future.delayed(new Duration(seconds: 3), () => 3);
  Future.wait({future1,future2,future3}).then(print).catchError(print);
}
testFuture();
print("在testFuture()执行之后打印。");
// 在testFuture()执行之后打印。
// Future 发生错误啦!

# Future.doWhile

重复性地执行某一个动作,直到返回false或者Future,退出循环,适用于一些需要递归操作的场景:

void testFuture() async {



    var random = new Random();



    var totalDelay = 0;



    Future.doWhile(() {



        if (totalDelay > 10) {



          print('total delay: $totalDelay seconds');



          return false;



        }



        var delay = random.nextInt(5) + 1;



        totalDelay += delay;



        return new Future.delayed(new Duration(seconds: delay), () {



          print('waited $delay seconds');



          return true;



        });



    }).then(print).catchError(print);



}



testFuture();



  



print("在testFuture()执行之后打印。");

执行结果:

在testFuture()执行之后打印。



waited 5 seconds



waited 5 seconds



waited 3 seconds



total delay: 11 seconds



null



waited 4 seconds



waited 3 seconds



total delay: 12 seconds



null

# Future.sync

会同步执行其入参函数,然后调度到microtask queue来完成自己。也就是一个阻塞任务,会阻塞当前代码,sync的任务执行完了,代码才能走到下一行:

void testFuture() async {



    Future((){



        print("Future event 1");



    });



    Future.sync(() {



        print("Future sync microtask event 2");



    });



    Future((){



        print("Future event 3");



    });



    Future.microtask((){



        print("microtask event");



    });



}



testFuture();



print("在testFuture()执行之后打印。");

执行结果:

Future sync microtask event 2testFuture()执行之后打印。



microtask event



Future event 1



Future event 3

但是注意,如果这个入参函数返回一个Future

void testFuture() async {



    Future((){



        print("Future event 1");



    });



    Future.sync(() {



        return Future(() {  print("Future event 2");



      });



    });



    Future((){



        print("Future event 3");



    });



    Future.microtask((){



        print("microtask event");



    });



}



testFuture();



print("在testFuture()执行之后打印。");

执行结果:

testFuture()执行之后打印。



microtask event



Future event 1



Future event 2



Future event 3

# Future.microtask

创建一个在microtask queue运行的Future。我们知道microtask queue的优先级是比event queue高的。而一般Future是在event queue执行的。所以Future.microtask创建的Future会优先于其他Future执行:

void testFuture() async {



    Future((){



        print("Future event 1");



    });



    Future((){



        print("Future event 2");



    });



    Future.microtask((){



        print("microtask event");



    });



}



testFuture();



print("在testFuture()执行之后打印。");

执行结果:

testFuture()执行之后打印。



microtask event



Future event 1



Future event 2

2)静态方法

(1)Future()

默认为timer

(2)delayed()

Future.delayed()执行一个延时任务。


void main(List<String> args) {   
    print('start....');   
    Future.delayed(Duration(seconds: 3), () => print("延时任务"));   
    print('end....');       
    // 执行结果:   
    // start....   
    // end....   
    // 延时任务 
}
 
//注: delayed 中实现的延时操作通过 Timer 来实现的,
//在实际开发中,如果只是一个单纯的延时操作,建议使用 Timer。
 
void main(List<String> args) {
  print('start....');
  new Timer(Duration(seconds: 3), () => print("延时任务"));
  print('end....');
 
  // 执行结果:
  // start....
  // end....
  // 延时任务
}
(3)value()

Future.value()创建一个返回指定 value 值的 Future,根据值选择,同时可以控制顺序

void main() {   
    print(1);   
    Future.value("abc").then((res) => print(res));   
    print(2);   
    // 执行结果   
    // 1   
    // 2     
    // abc 
}
(4)sync()

​ Future.sync() - 同步在主线执行

(5)microtask()

​ Future.microtask() - 转换微任务

(6)wait()

Future.wait()执行多个 Future,等待所有的 Future 执行完成。

void main() {   
Future.wait([futureA(), futureB()])
    .then((res) {     
        print(res);   
    }); 
} 
 
Future futureA() {   
    return Future.delayed(Duration(seconds: 3), () {     
        print("futureA 完成了");   
    }); 
} 
Future futureB() {   
    return Future.delayed(Duration(seconds: 5), () {     
        print("futureB 完成了");   
    }); 
} 
// 执行结果 
// futureA 完成了 
// futureB 完成了 
// [null, null]
(7)any()

Future.any()执行多个 Future,哪个先执行完成,就返回对应的数据。

void main() {   
Future.any([futureA(), futureB()])
    .then((res) {     
        // 此时 res 获取的是 futureA() 执行的结果     
        print(res);   
    }); 
} 
 
Future futureA() {   
    return Future.delayed(Duration(seconds: 3), () {     
        print("futureA 完成了");   
    }); 
} 
 
Future futureB() {   
    return Future.delayed(Duration(seconds: 5), () {     
        print("futureB 完成了");   
    }); 
} 
// 执行结果 
// futureA 完成了 
// null         -----------  Future.any 返回的结果 
// futureB 完成了

4. Stream

Stream是一个异步的事件队列,也是响应式编程的实现。

1)流

  • 编程核心就是处理数据,从上游拿到数据,经过处理后传递给下游。随着处理的数据越来越多,就有了集合,包装了一系列数据在上下游之间进行传递。
  • 业务数据的处理可能要跨多个线程,这样数据处理流程就从单个线程的同步处理变成跨多个线程的异步处理。此时集合通常充当多个线程之间协作的缓冲池,当缓冲池满了的时候上游线程阻塞,空了的时候下游线程阻塞。
  • 在开发过程中经常需要开发人员写很多遍历数据集合的操作,就出现了集合的迭代器来简化遍历。在遍历过程中常会有筛选、排序、映射转换、合并或者拆分等操作。

流的出现就是简化针对集合的这些操作,方便开发。流关注的是针对数据源的一系列操作而不是数据的存储。所以流就是一个数据源+管道(多个中间操作)+ 终端操作组成。

  • 数据源:定义流操作的原始数据是哪里来的,可以是集合、数组、生成器函数、io管道以及其他流作为数据来源。
  • 中间操作:针对流中的数据进行处理,处理产生的结果还是流:比如过滤filter、一对多flatMap、一对一的变换map、排序sorted等等操作。中间操作又被分为:
    • 有状态的中间操作:比如排序、去重等操作,需要知道整个序列的所有元素才可以完成操作。
    • 无状态的中间操作:比如过滤、映射等操作,单个元素就可以完成操作,不需要知道其他元素的状态。所以这种中间操作是可以并行处理的。
  • 终端操作:定义了流中元素最终要经历的操作,比如最大最小值、个数统计、转换成集合或其他类型、提供对元素遍历的接口等。终端操作的执行意味着定义的流开始消耗,等最后一个元素经历过终端操作后,这个流就相当于完成了。
  • 短路操作:如果在提供无限流输入时,它可能会产生一个有限的流,那么他就是短路的。如果在无限流作为输入时,它可能在有限的时间内终止,这个终端操作是短路的。
  • 流的中间操作是惰性的:中间操作并不会立即执行,而是等终端操作开始执行时才会被执行。
  • 流不存储数据,流的中间操作并不修改数据源的结构。流的短路操作让其可以处理无线数据源。流的延迟特性让其可以优化中间操作。
  • 流的惰性让流可以处理异步事件。

Future和Stream是Dart异步库中的核心,Future代表的是单个异步事件,Stream代表的是一个异步事件序列

Stream 是 Dart 中用于处理数据流的机制,它可以用来处理连续产生的数据序列,例如网络请求的流式响应、事件流等。通过 Stream,可以实现订阅数据流、监听事件、处理数据等操作。

import 'dart:async';

void main() {
  Stream<int> counterStream = countStream(5); // 创建一个计数器数据流
  counterStream.listen((data) {
    print('Count: $data');
  });
}

Stream<int> countStream(int maxCount) async* {
  for (int i = 0; i < maxCount; i++) {
    await Future.delayed(Duration(seconds: 1)); // 模拟延迟1秒
    yield i; // 发送数据到数据流
  }
}

2)创建流

(1)生成器创建

async 标记的方法称为异步生成器,在其中可以通过yield生成单个元素,yield生成多个元素,最终汇集成流

Stream<String> createStream() async* {   
    for (int i = 0; i < 100; i++) {     
        await Future.delayed(Duration(seconds: 1));     
        yield "第${i}份数据";   
    } 
} 
 
main() {   
    Stream<String> result = createStream();   
    Future future = result.forEach((element) {     
        print(element);   
    });   
    print('future开始!'); 
}
(2)流命名构造函数创建

根据已有的数据序列创建流序列。

Stream.fromIterable(Iterable<T> elements)

根据Future创建流

Stream.fromFuture(Future<T> future) Stream.fromFutures(Iterable<Future<T>> futures)
(3)StreamController创建流

其实上面(2)中创建流的方式底层都是通过StreamController来创建的。

StreamController<int> controller = StreamController(); 
Stream<int> stream = controller.stream;

3)流的订阅

Stream可以通过listen方法来创建流的订阅,需要回调函数。返回的是订阅类StreamSubscription,订阅流可以监听流中的数据或事件。

(1)单订阅流

​ 默认情况下创建的流都是单订阅流,单订阅流只能被订阅一次,第二次监听会报错!监听开始之前的元素不会被订阅。但 Stream 可以通过 transform() 方法(返回另一个 Stream)进行连续调用。通过 Stream.asBroadcastStream() 可以将一个单订阅模式的 Stream 转换成一个多订阅模式的 Stream,isBroadcast 属性可以判断当前 Stream 所处的模式。

(2)广播订阅

​ 广播流允许存在任意数量的 listener,并且无论是否存在 listener,它都能产生事件,所以中途加入的 listener 不会侦听到已发生的事件。

单订阅流常用于数据的传递,广播流用于事件的分发。

4)常用方法

(1)Sink

Sink是流控制器最基础的接口,定义了一个通用的数据接收器所需要的方法

(2)EventSink

EventSink是对Sink的扩展,添加了处理Error的方法。\

(3)StreamConsumer

StreamConsumer 定义了可以接受流输入的槽\

(4)StreamSink

StreamSink定义了可以同步或异步接收流事件的接口方法。

(5)StreamController

StreamController 是流控制器的核心接口,包含了流控制器该有的大多数接口方法:

  • stream用于向外提供创建的Stream。
  • sink 是该控制器关联的流的数据来源。可以使用sink.add 方法向流中添加数据。
  • onListen, 当控制器中的流被监听的时候,会回调该方法。
  • onPause, 当流的监听主动暂停的时候,会回调该方法。
  • onResume, 当流的监听主动恢复监听的时候,会回调该方法。
  • onCancel,当流的监听取消监听的时候,会回调该方法。

流控制器中有两个工厂构造函数,分别构造了单订阅,和广播流以及同步和异步流。

5)案例

(1)基本使用
import 'dart:async';
 
/// stream 主要是为了实现消息通信方案
void main(){
  StreamController controller = StreamController();
  controller.stream.listen((event) {
    print("接受到数据:$event");
  });
 
  // //单一订阅模式下二次监听报错
  // controller.stream.listen((event) {
  //   print("接受到数据:$event");
  // });
 
  controller.sink.add("abc");
  controller.sink.add("123");
}

(2)订阅模式-广播
import 'dart:async'; 
/// stream 主要是为了实现消息通信方案 
void main(){   
    StreamController controller = StreamController();   
    controller.stream.listen((event) {     
        print("接受到数据:$event");   });   
        // //单一订阅模式下二次监听报错   
        // controller.stream.listen((event) {   
        //   print("接受到数据:$event");   
    // });   
    controller.sink.add("abc");   
    controller.sink.add("123"); 
}
(3)stream创建方案

import 'dart:async';
 
/// stream 创建方案
void main(){
  testFromFuture();
  // testFromFutures();
  // testFromIterable();
  // testPeriodic1();
  // testPeriodic2();
}
 
Future<String> getData(){
  return Future.delayed(Duration(seconds: 5),(){
    return "当前时间:${DateTime.now()}";
  });
}
 
 
void testFromFuture(){
  Stream.fromFuture(getData())
      .listen((event) {
        print("event:$event");
      })
      .onDone(() {
        print("done ");
      });
}
 
void testFromFutures(){
  Stream.fromFutures([getData(),getData(),getData()])
      .listen((event) {
        print("event:$event");
      })
      .onDone(() {
        print("done ");
      });
}
 
void testFromIterable(){
  var datas = [1,'hello',2,'he',5];
  Stream.fromIterable(datas)
      .listen((event) {
        print("event:$event");
      })
      .onDone(() {
        print("done ");
      });
}
 
void testPeriodic1(){
  Duration interval = Duration(seconds: 2);
  int i = 0;
  Stream.periodic(interval)
      .listen((event) {
        print("event:$event");
      })
      .onDone(() {
        print("done ");
      });
}
 
void testPeriodic2(){
  Duration interval = Duration(seconds: 2);
  int i = 0;
  //take(5) 指定最大提取
  Stream.periodic(interval,(data)=>data)
      .listen((event) {
        print("event:$event");
      })
      .onDone(() {
        print("done ");
      });
}
(5)API使用

import 'dart:async';
 
/// stream 创建方案
void main() {
  // testTake();
  // testWhile();
  // testWhere();
  // testDistinct();
  // testSkip();
  testSkipWhile();
}
 
void testTake() {
  Duration interval = Duration(seconds: 1);
  int i = 0;
  //take(5) 指定最大提取
  Stream.periodic(interval, (data) => data).take(5).listen((event) {
    print("event:$event");
  }).onDone(() {
    print("done ");
  });
}
 
///循环
void testWhile() {
  Duration interval = Duration(seconds: 1);
  int i = 0;
  //take(5) 指定最大提取
  Stream.periodic(interval, (data) => data).takeWhile((element) {
    print("takeWhile element:$element");
    return element <= 5;
  }).listen((event) {
    print("event:$event");
  }).onDone(() {
    print("done ");
  });
}
 
/// 条件过滤
void testWhere() {
  Duration interval = Duration(seconds: 2);
  int i = 0;
  //take(5) 指定最大提取
  Stream.periodic(interval, (data) => data)
      .takeWhile((element) {
        return element <= 5;
      })
      .where((data) => data % 2 == 0)
      .listen((event) {
        print("event:$event");
      })
      .onDone(() {
        print("done ");
      });
}
 
/// 去重
void testDistinct() {
  var data = [1, 2, 'a', 'a', 1, 1, 3, 4];
  Stream.fromIterable(data).distinct().listen((event) {
    print("testDistinct listen:$event");
  }).onDone(() {
    print("testDistinct done");
  });
}
 
void testSkip() {
  var data = [1, 2, 'a', 'a', 1, 1, 3, 4];
  Stream.fromIterable(data).skip(2).listen((event) {
    print("testDistinct listen:$event");
  }).onDone(() {
    print("testDistinct done");
  });
}
 
void testSkipWhile() {
 
  Stream.periodic(Duration(seconds: 1),(data) => data)
  .takeWhile((element) => element <= 6)
  .skipWhile((element){
    return element <= 3;
  }).listen((event) {
    print("testDistinct listen:$event");
  }).onDone(() {
    print("testDistinct done");
  });
}

5.async和await

asyncawait 是 Dart 中用于简化异步代码编写的关键字。async 用于修饰异步函数,表示该函数中可能会包含 await 关键字等待其他异步操作的完成。await 关键字用于等待一个异步操作完成,并获取其结果。

  • async 是让方法变成异步方法。用来表示函数是异步的,定义的函数会返回一个Future对象。只有 async 方法才能使用 await 关键字去调用方法
  • await 后面跟着一个Future,表示等待该异步任务完成,异步任务完成后才会继续往下执行。await只能出现在异步函数内部。能够像写同步代码那样来执行异步任务而不使用回调的方式。如果调用别的 async 方法必须使用 await 关键字

async 需要写在方法括号的后面,await 写在方法里面,await 关键字必须在 async 函数内部使用,不然会报错。await 表达式可以使用多次。在Dart中,async/await都只是一个语法糖,编译器或解释器最终都会将其转化为一个Promise(Future)的调用链。

// 导入io库,调用sleep函数
import 'dart:io';

// 模拟耗时操作,调用sleep函数睡眠2秒
doTask() async {
  sleep(const Duration(seconds: 2));
  return "Ok";
}

// 定义一个函数用于包装
test() async {
  var r = await doTask();
  print(r);
}

void main() {
  print("main start");
  test();
  print("main end");
}

在函数签名中加入async关键字,表示该函数异步执行,await表示等待异步结果执行完成返回Future对象。但有一点需要注意,await只能在async函数中出现,因此往往需要再定义一个async函数,用于包装。上述代码中test函数就是用于包装。

import 'dart:io';

main() {                                           
  test1();            //调用test1()方法,要使用await关键字,否则若有返回值会报错
  test2();            //调用test2()方法
  print("----over-----");
}

test1() async {          //此方法为async异步方法
  print("test---1");
  await test3();   //调用了异步方法test3(),(注意如果test3()有返回值,这里必须用await关键字调用)
  print("test---1.1");
}

test2() { 
  print("test---2");
}

test3() async {          //声明test3()方法为异步方法
  print("test---3");
}

结果:
test---1
test---3
test---2
----over-----
test---1.1

解释:在main()方法中依次调用了test1 ( )方法和test2( )方法,test1()方法为异步方法,故不需要test1()方法执行完才开始执行test2()方法,在test1()方法中调用了异步方法test3( ),这里使用了await关键字来调用test3( )方法,此时test2( )方法同时在执行。

三、元数据

使用元数据给代码添加更 的信息 元数据是以 @开始 饰符,在@后面接着编译时的常量或调用一个常量构造函数。目前 Dart 提供三个@修饰符

@precated 被弃用的

@override 重写

@proxy 代理

元数据可以修饰 library (库) class (类) typedef 类型定义)、 type parameter (类型参数) constructor (构造函数)、 facto ry (工厂函数) function (函数) field (作用域)、parameter (参数) variable claration (变量声明)

t
// 导入io库,调用sleep函数
import ‘dart:io’;

// 模拟耗时操作,调用sleep函数睡眠2秒
doTask() async {
sleep(const Duration(seconds: 2));
return “Ok”;
}

// 定义一个函数用于包装
test() async {
var r = await doTask();
print®;
}

void main() {
print(“main start”);
test();
print(“main end”);
}


在函数签名中加入`async`关键字,表示该函数异步执行,`await`表示等待异步结果执行完成返回`Future`对象。但有一点需要注意,`await`只能在`async`函数中出现,因此往往需要再定义一个`async`函数,用于包装。上述代码中`test`函数就是用于包装。 

~~~dart
import 'dart:io';

main() {                                           
  test1();            //调用test1()方法,要使用await关键字,否则若有返回值会报错
  test2();            //调用test2()方法
  print("----over-----");
}

test1() async {          //此方法为async异步方法
  print("test---1");
  await test3();   //调用了异步方法test3(),(注意如果test3()有返回值,这里必须用await关键字调用)
  print("test---1.1");
}

test2() { 
  print("test---2");
}

test3() async {          //声明test3()方法为异步方法
  print("test---3");
}

结果:
test---1
test---3
test---2
----over-----
test---1.1

解释:在main()方法中依次调用了test1 ( )方法和test2( )方法,test1()方法为异步方法,故不需要test1()方法执行完才开始执行test2()方法,在test1()方法中调用了异步方法test3( ),这里使用了await关键字来调用test3( )方法,此时test2( )方法同时在执行。
~~~



# 三、元数据

使用元数据给代码添加更 的信息 元数据是以 @开始 饰符,在@后面接着编译时的常量或调用一个常量构造函数。目前 Dart 提供三个@修饰符

@precated 被弃用的

@override 重写

@proxy 代理

元数据可以修饰 library (库) class (类) typedef 类型定义)、 type parameter (类型参数) constructor (构造函数)、 facto ry (工厂函数) function (函数) field (作用域)、parameter (参数) variable claration (变量声明)





































  • 19
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木颤简叶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值