前面在Flutter之旅:Dart语法扫尾-包访问-泛型--异常-异步-mixin中向你们说过:
会有一篇专门介绍Dart中异步的文章,如今如约而至,我将用精致的图文加上生动的例子向你阐述
各位,下面一块儿来看看吧。程序员
程序同步是按顺序执行:一个任务执行完才能进入下一个任务,
就像下面的代码,扫地用了15分钟,而后才能烧水,必须等水开了才能洗衣服。数据库
main() {
print("A任务: 扫地 15min");
print("B任务: 烧水 25min");
print("C任务: 洗衣服 25min");
}
复制代码
若是把一我的看做劳动力,那么这样执行会减小劳动力的利用率。
对于残酷的剥削者而言,这样的工做方式显然是不能让他满意的:
彻底能够先烧水,开火以后去扫地,扫完地倒垃圾,而后再洗衣服,
等到水开了,停下洗衣服的动做,冲完水再去洗衣服,这才是剥削者的思路数组
CPU就是那个劳动力,而程序员就是残酷的剥削者。为了让它能卖命的工做,就产生了异步
当咱们须要链接网络,读取文件,数据库操做等耗时操做,就像在等水烧开
你确定不想一个劳动力傻傻站那等水开吧,因此你要告诉它,如今去洗衣服,水开了再来冲水
因而就涉及到了一个问题,我怎么知道谁烧开了呢?这是发生在将来的不肯定时间点的事件
因而须要搞点东西来标识一下,就像水开了会呜呜响,否则的话,一直洗衣服,还不烧干了?bash
在读取文件的时候,经过File对象的readXXX方法,你会惊奇的发现:
没有Sync后缀的方法名都是一个Future对象,它代表该操做返回的是一个将来的对象
在将来的对象,如今固然还拿不到,那怎么用呢?能够看到Future有一个then方法微信
---->[sky_engine/lib/async/future.dart:601]----
Future<R> then<R>(FutureOr<R> onValue(T value), {Function onError});
复制代码
该方法上注释以下: then方法用来注册未来完成时要调用的回调。
当这个future使用一个值完成时,将该值在[onValue]中回调。
若是这个future已经完成,那么回调将不会当即调用,而是将在稍后的微任务中调度。
另外能够看到一个可选参数onError,当执行错误时会进行错误回调网络
既然知道then中能够传递一个回调来获取文件内容,那就简单了
看下图的结果,能够感觉到读取文件是异步的,文件读取的代码在上,运行时在下面
说明该程序在读取文件这个耗时操做时,先执行后面代码,读取完成后才执行then的回调app
import 'dart:io';
main() {
var path = '/Volumes/coder/Project/Flutter/flutter_journey/lib/day6/漫感.txt';
Future<String> futureStr = File(path).readAsString();
futureStr.then((value){
print(value);
});
print("=======看看控制台,我是第在哪里?======");
}
复制代码
async
和await
异步读取文件给一个方法名加上
async
标注,就说明该方法是异步方法,其中能够执行异步操做
好比异步读取文件,只须要在Future对象前加上await,便可获取将来的值。dom
import 'dart:io';
main() {
readByAsync();
print("=======看看控制台,我是第在哪里?======");
}
readByAsync() async{
var path = '/Volumes/coder/Project/Flutter/flutter_journey/lib/day6/漫感.txt';
var result = await File(path).readAsString();
print(result);
}
复制代码
同步读取就像等着烧开水,完成再去作别的事,读取文件接收才能执行下一行代码异步
main() {
readBySync();
print("=======看看控制台,我是第在哪里?======");
}
readBySync() {
var path = '/Volumes/coder/Project/Flutter/flutter_journey/lib/day6/漫感.txt';
var result = File(path).readAsStringSync();
print(result);
}
复制代码
Stream流也不是什么新鲜的玩意了,各大语言基本上都有流的操做,
这里就Dart中的Stream流进行详细的阐述。首先看Stream的几个建立方法async
factory Stream.empty() = _EmptyStream<T>//建立一个空的流
Stream.fromFuture(Future<T> future)//由一个Future对象建立
Stream.fromFutures(Iterable<Future<T>> futures)//由多个Future对象建立
Stream.fromIterable(Iterable<T> elements)//由可迭代对象建立
Stream.periodic(Duration period,[T computation(int computationCount)])//有周期的流
复制代码
我以为Stream的认知中最重要的是区别它和列表有什么不一样,下面先亲身体验一下
普通列表遍历
var fishes = ["A", "B", "C"];
fishes.forEach((e){
print(e);
});
print("====");
---->[打印结果]----
A
B
C
====
复制代码
流遍历
void byStream() {
var fishes = ["A", "B", "C"];
var stream =Stream.fromIterable(fishes);
stream.forEach((e){
print(e);
});
print("====");
}
---->[打印结果]----
====
A
B
C
复制代码
不知有心人是否看出二者的区别:Stream在遍历的时候竟然是异步的,这就是它和列表最大的不一样
一个List在遍历的那一刻,我就知道里面是什么,有多少元素,能够怎么这么操做它。
List就像后宫佳丽三千都在宫里等你随时操做,Stream则是后宫佳丽三千正在赶来的路上,你再急也没办法。
算了,换个例子,List就像鱼缸,里面盛着鱼,你知道鱼就在那,并且随时能够拿出来吃了
Stream像一条小溪,你只是知道里面的鱼在向你游来,在这一刻你不能捞出它们,
何时游到你这里也未知,对你而言它们都是你将来的财富。
话说这样有什么用
如今,邪恶的我在鱼游动的过程当中偷偷给A下毒,而后将来你拿到A后吃掉就傻傻的死掉
这就是Stream中的元素到达目的地以前,均可以进行控制和操做,我黑你几条鱼你也不知道。
listen
也就是站在前面的你,在等待着鱼过来。说明你订阅了这个流中的元素。
在风平浪静,没人下毒的状况下,将来你必定能拿到河里向你游来的这三条鱼。
var fishes = ["A", "B", "C"];
var stream =Stream.fromIterable(fishes);
stream.listen((fish)=>print("拿到了$fish"));
---->[打印结果]----
拿到了A
拿到了B
拿到了C
复制代码
订阅的回调
var fishes = ["A", "B", "C"];
var stream = Stream.fromIterable(fishes);
stream.listen((fish) => print("拿到了$fish"),
onDone: () => print("已所有拿到"),//完成回调
onError: () => print("产生错误"),//错误回调
cancelOnError: false);//错误时是否取消订阅
复制代码
一旦订阅取消成功,onDone不会回调,即便你已经拿到了最后一条鱼
下面就说明你在拿到B后,你就取消订阅,走人
var fishes = ["A", "B", "C"];
var stream = Stream.fromIterable(fishes);
var you = stream.listen(null);//你订阅了这条小溪
you.onData((fish){//声明鱼到达你那里你的行为
print("拿到了$fish");
if(fish=="B"){//拿到B后,你就取消订阅,走人
you.cancel();
}
});
you.onError((e)=>print("产生错误$e"));
you.onDone(()=>print('已所有拿到'));
复制代码
里面就只有三条鱼,你感受很不爽,这时善良的管理员说,我如今就给你加
StreamController中有一个stream对象,能够经过它进行流的操做
因为是异步的,能够在订阅后继续添加,也是不影响你对数据的获取
就像你订阅以后,管理员将鱼放在水里,鱼也会游到你的面前。
StreamController controller = StreamController();
controller.add("A");
controller.add("B");
controller.add("C");
controller.stream.listen((fish) => print("拿到了$fish"));
controller.add("D");
controller.add("E");
controller.add("F");
controller.close();
复制代码
邪恶的我来了,在中游截获一条条鱼。记住这幅图,Stream流的思想就差很少了。
StreamController controller = StreamController();
controller.add("A");
controller.add("B");
controller.add("C");
controller.stream
.map((fish) {//每条鱼都从我面前游过
if (fish == "C") {
print("我已经已经对C下毒");
return "中毒的C";
}
if(fish=="D"){
print("D已经被我吃完了");
return "D的骨头";
}
return fish;
})
.skip(2)//扔掉前两个
.take(2)//最终只能拿两个
.listen((fish) => print("傻傻的你拿到了$fish"));
controller.add("D");
controller.add("E");
controller.add("F");
controller.close();
---->[打印结果]----
我已经已经对C下毒
傻傻的你拿到了中毒的C
D已经被我吃完了
傻傻的你拿到了D的骨头
复制代码
当鱼塘里加到B鱼以后,你朋友和你站在一块儿,也订阅了,这时候他只能监听到以后添加的。
使用broadcast方法可让一个流被多人监听,不然异常:Stream has already been listened to.
StreamController<String> controller = StreamController<String>.broadcast();
StreamSubscription you =
controller.stream.listen((value) => print('监听到 $value鱼游到你身边'));
controller.sink.add("A");
controller.sink.add("B");
StreamSubscription youFriend =
controller.stream.listen((value) => print('监听到 $value鱼游到你朋友身边'));
controller.sink.add("C");
controller.sink.add("D");
controller.close();
复制代码
在Dart中文件的顶层为FileSystemEntity抽象类,其下有三个孩子:
File接口,Directory接口,Link接口,其中三个各有一个私有类分别继承之
---->[构造方法]----
Directory(String path)//从路径
Directory.fromUri(Uri uri)//从uri
Directory.fromRawPath(Uint8List path)//从原生路径
Uri get uri;
Directory get current;
Directory get absolute;
---->[异步操做]----
Future<Directory> create({bool recursive: false});//建立文件夹
Future<Directory> createTemp([String prefix]);//建立临时文件夹
Future<Directory> rename(String newPath);//重命名
Stream<FileSystemEntity> list(//遍历
{bool recursive: false, bool followLinks: true});
---->[同步操做]----
void createSync({bool recursive: false});
Directory createTempSync([String prefix]);
Directory renameSync(String newPath);
Stream<FileSystemEntity> list(
{bool recursive: false, bool followLinks: true});
复制代码
var dir=Directory(path);
print(dir.path);//Volumes/coder/Project/Flutter/flutter_journey/lib/day6/data
print(Directory.current.path);//当前项目磁盘路径:/Volumes/coder/Project/Flutter/flutter_journey
print(dir.absolute.path);//Volumes/coder/Project/Flutter/flutter_journey/lib/day6/data
dir.createTemp("-");//随机建立自定义前缀的一个文件夹,
dir.list(recursive: true).forEach((e){
print(e.path);
}).then((v){
print("遍历完毕");
});
print("----");//验证list方法为异步
复制代码
文件操做相关
---->[异步操做]----
Future<File> create({bool recursive: false}); //异步建立一个文件(是否递归)
Future<File> rename(String newPath);//异步重命名文件
Future<File> copy(String newPath);//异步拷贝文件到新路径
Future<RandomAccessFile> open({FileMode mode: FileMode.read});//异步打开文件
---->[同步操做]----
void createSync({bool recursive: false});//同步建立一个文件(是否递归)
File renameSync(String newPath);//同步重命名文件
File copySync(String newPath);//同步拷贝文件到新路径
RandomAccessFile openSync({FileMode mode: FileMode.read});//同步打开文件
复制代码
不知简写成下面的样子你们可不能够接受,这是Future对象的链式调用
咱们能够看到create返回的还是一个Future对象,也就是说then方法的回调值还是File对象
你就能够继续调用相应的异步方法再进行then,再回调,再then,是否是颇有趣。
var path =
'/Volumes/coder/Project/Flutter/flutter_journey/lib/day6/data/应龙.txt';
var pathCopy =
'/Volumes/coder/Project/Flutter/flutter_journey/lib/day6/data/应龙-copy.txt';
var pathRename =
'/Volumes/coder/Project/Flutter/flutter_journey/lib/day6/data/应龙-rename.txt';
var file = File(path);
file
.create(recursive: true)
.then((file) => file.copy(pathCopy)
.then((file) => file.rename(pathRename)
.then((file)=>print("建立,拷贝,重命名完毕"))));
复制代码
文件信息相关
这一组没什么好说的,顾名思义,须要的时候知道有这些API就好了
---->[异步操做]----
Future<int> length();//异步获取文件大小
Future<DateTime> lastAccessed();//异步获取最后访问时间
Future setLastAccessed(DateTime time);//异步设置最后访问时间
Future<DateTime> lastModified();//异步获取最后修改时间
Future setLastModified(DateTime time);//异步设置最后修改时间
---->[同步操做]----
int lengthSync();//同步获取文件大小
DateTime lastAccessedSync();//同步获取最后访问时间
void setLastAccessedSync(DateTime time);//同步设置最后访问时间
DateTime lastModifiedSync();//同步获取最后修改时间
void setLastModifiedSync(DateTime time);//异步设置最后修改时间
File get absolute;//获取绝对文件
String get path;//获取路径
Directory get parent => new Directory(parentOf(path));//获取父文件
复制代码
文件读写相关
文件的读写可谓是重中之重
IOSink openWrite({FileMode mode: FileMode.write, Encoding encoding: utf8});
---->[异步写操做]----
Future<File> writeAsBytes(List<int> bytes,
{FileMode mode: FileMode.write, bool flush: false});
Future<File> writeAsString(String contents,
{FileMode mode: FileMode.write,Encoding encoding: utf8,bool flush: false});
---->[同步写操做]----
void writeAsBytesSync(List<int> bytes,
{FileMode mode: FileMode.write, bool flush: false});
void writeAsStringSync(String contents,
{FileMode mode: FileMode.write,Encoding encoding: utf8,bool flush: false});
Stream<List<int>> openRead([int start, int end]);
---->[异步读操做]----
Future<List<int>> readAsBytes();
Future<String> readAsString({Encoding encoding: utf8});
Future<List<String>> readAsLines({Encoding encoding: utf8});
---->[同步读操做]----
List<int> readAsBytesSync();
String readAsStringSync({Encoding encoding: utf8});
List<String> readAsLinesSync({Encoding encoding: utf8});
复制代码
openWrite方法
其一,它返回了一个IOSink对象;其二,它就收模式和编码两个入参
这里测试了一下,它能够自动建立文件并写入字符,注意它并不能自动建立文件夹
var path =
'/Volumes/coder/Project/Flutter/flutter_journey/lib/day6/data/应龙-openWrite.txt';
var file=File(path);
file.openWrite().write("应龙");
复制代码
其中返回的IOSink对象有几个方法能够对不一样的的类型进行写入,好比数组
在写入时能够自定义分隔符
var li=["Java","Dart","Kotlin","Swift"];
file.openWrite().writeAll(li,"¥¥");
---->[结果]----
Java¥¥Dart¥¥Kotlin¥¥Swift
复制代码
默认状况下是
FileMode.write
,名称写入都会先将原来的内容清空,除此以外,还有:
FileMode.write//打开可读写文件,会覆盖已有文件
FileMode.append//打开可读写文件,日后追加
FileMode.writeOnly//打开只写文件,会覆盖已有文件
FileMode.writeOnlyAppend//打开只写文件,日后追加
复制代码
openRead返回一个Stream<List>对象,它和Future比较像,有一个listen回调方法
它能够回调多个将来的对象的序列 ,你能够测试一下,它也是异步的
这里回调出的是一个List,也就是对应的字节在码表中的数值集合。
var path =
'/Volumes/coder/Project/Flutter/flutter_journey/lib/day6/data/应龙-openRead.txt';
file.openRead().listen((li) => li.forEach((e) => print(String.fromCharCode(e))));
复制代码
能够看到openRead方法中有两个不定参数,能够控制读取的起止点
至于为何这样作:若是一个很是大的文件经过readAsString,那么会一次加载到内存中
若是内存不足就会崩掉,Stream就像是细水长流,一点一点进行读取。
var path =
'/Volumes/coder/Project/Flutter/flutter_journey/lib/day6/data/应龙-openRead.txt';
file.openRead().listen((li) => li.forEach((e) => print(String.fromCharCode(e))));
复制代码
另外的一些方法,使用上都大同小异,就不赘述了。
本文到此接近尾声了,若是想快速尝鲜Flutter,《Flutter七日》会是你的必备佳品;若是想细细探究它,那就跟随个人脚步,完成一次Flutter之旅。
另外本人有一个Flutter微信交流群,欢迎小伙伴加入,共同探讨Flutter的问题,本人微信号:zdl1994328
,期待与你的交流与切磋。