Dart语法摘要(四)

学Flutter就和学iOS一样,先学基本语言语法的使用,再学习搭UI,iOS是OC和Swift,Flutter么就是Dart,语言学多了语法都差球不多,特别是这些新的语言,很多东西都是相似的,但是长时间不用容易忘记,与其每次去网上瞎jb找,不如自己总结一些,以后看自己写的东西就行了。文中个别结论是我自己总结出来的,不能保证准确性,看到的同学仅供参考。

本章提纲:
1.异步
2.泛型
3.库的使用

异步

Dart 库中包含许多返回 Future 或 Stream 对象的函数。这些函数在设置完耗时任务后, 就立即返回了,不会等待耗任务完成。 使用 asyncawait 关键字实现异步编程。 可以让你像编写同步代码一样实现异步操作。

Future

Future的字面意思就是在未来的某一个时刻让你拿到结果。
Future有两种状态:

  • 状态一:未完成状态(uncompleted)
    执行Future内部的操作时(在上面的案例中就是具体的网络请求过程,我们使用了延迟来模拟),我们称这个过程为未完成状态
  • 状态二:完成状态(completed)
    当Future内部的操作执行完成,通常会返回一个值,或者抛出一个异常。
    这两种情况,我们都称Future为完成状态。
mport "dart:io";

main(List<String> args) {
  print("main function start");
  print(getNetworkData());
  print("main function end");
}

Future<String> getNetworkData() {
  return Future<String>(() {
    sleep(Duration(seconds: 3));
    return "network data";
  });
}

// 打印
// main function start
// Instance of 'Future<String>'
// main function end

使用.then来拿到Future的回调或者使用.catchError(error)来捕获异常,whenComplete指所有任务完成后的回调函数,类似于finally:

import "dart:io";

main(List<String> args) {
  print("main function start");
  var future = getNetworkData();
  future.then((value) {
    print(value);
  }).catchError((error) { // 捕获出现异常时的情况
    print(error);
  }).whenComplete(() {
    print("whenComplete")//所有任务完成后的回调函数;
  });
  print(future);
  print("main function end");
}

Future<String> getNetworkData() {
  return Future<String>(() {
    sleep(Duration(seconds: 3));
    future.then((value) {
      print(value);
    });
    // 不再返回结果,而是出现异常
    // return "network data";
    // throw Exception("网络请求出现错误");
  });
}

Future.then链式调用避免了回调地狱问题,这个感觉就跟OC里的Block的链式调用很相似:

void main() {
  login('tong', '1234556').then((id){
    print('登录成功,用户id为${id}');
    return getUserInfo(id);
  }).then((userInfo){
    print('获取用户信息成功,结果为${userInfo}');
    return saveUserInfo(userInfo);
  }).then((data){
    print('保存用户信息成功');
  });

Future.value(value)可以直接获取一个完成的Future,该Future会直接调用then的回调函数:

main(List<String> args) {
  print("main function start");

  Future.value("哈哈哈").then((value) {
    print(value);
  });

  print("main function end");
}
// 打印
// main function start
// main function end
// 哈哈哈

这块为啥直接获取到完成的Future,哈哈哈还是最后才打印呢,因为.then回调中的事件被加到eventLoop中了,简单说下自己对这个eventLoop的理解吧,这玩意儿就和iOS里的runLoop差不多,本质上都是一套事件循环机制,只不过iOS里的runLoop有主线程和子线程之分,但在Dart里我看文章里讲的似乎只有一条线程,Dart中的异步操作也并不像iOS中可以去新开一条线程去执行一些耗时操作,这里的Future其实是个非阻塞调用的感觉,就是说我当前线程要执行一些耗时操作,那我把调用完这个返回Future的耗时操作方法以后就不管了,“主线程”不会在这边等待结果,继续执行下边的操作,至于结果什么时候回来,我就通过.then去挂一个监听,等回调回来的时候,.then里面的方法会被加到当前这个eventLoop的队列里等待执行,所以上面的哈哈哈就是被放在eventLoop里,当main function end打印完后就打印哈哈哈,大概就这么个意思,这点像我之前接触了iOS的多线程机制还不太能理解单线程是怎么执行异步操作的,其实这个单线程也就是个“伪单线程”,只不过耗时操作在哪个线程执行交给CPU去调度了,我们只需要关注什么时候拿到结果就OK了,看完这块其实也加深了对OC中async和sync的理解,async严格意义上就是个非阻塞调用方式,只不过在OC里这种非阻塞调用的方式可以创建一条新的线程而已,目前是这么理解的,可能不一定对,后面讲到Dart其实也可以自己去创建一个新的线程,只不过他这个不单单是一条线程,他叫Isolate,包含了线程和evetLoop,后面再讲讲。

await、async

使用 await 可以让当前操作等待后面异步函数的执行结果,使用 await 时代码必须在使用 async 标记的函数中,使用async标记的函数,必须返回一个Future对象,这个对象并不需要我们手动包装,直接返回正常的数据,返回值会默认被包装在一个Future对象中,这个特性好像Swift哪个语法点里也有,我记得可以自动包装来着,完了看Swift的时候再看看吧。

Future<String> getNetworkData() async {
  var result = await Future.delayed(Duration(seconds: 3), () {
    return "network data";
  });

  return "请求到的数据:" + result;//自动包装成Future
}

虽然异步函数可能会执行耗时的操作, 但它不会等待这些操作。 相反,异步函数只有在遇到第一个 await 表达式时才会执行。 也就是说,它返回一个 Future 对象, 仅在await表达式完成后才恢复执行。

Dart关于异步的基本用法目前就这些,还有些高级用法后面用到的时候再看吧。

try / catch / finally

和Java中的try/catch类型,在OC也有捕获异常的机制,就是平时开发的时候没怎么用过:

try {
  breedMoreLlamas();
} on OutOfLlamasException {
  // 一个具体异常
  buyMoreLlamas();
} on Exception catch (e) {
  // 任意一个异常
  print('Unknown exception: $e');
} catch (e) {
  // 非具体类型
  print('Something really unknown: $e');
} finally {
   print('Something complete');
}

泛型

OC应该是从Swift2.0的时候开始为了更好的兼容Swift,引入了泛型,但我比较lowb,对OC泛型的使用还是仅仅停留在声明集合类型对象的时候,其他用法涉猎不多,慢慢的接触Swift和Dart的时候才接触到泛型的其他使用场景。

为什么使用泛型

在类型安全上通常需要泛型支持, 它的好处不仅仅是保证代码的正常运行:

  • 正确指定泛型类型可以提高代码质量。
  • 使用泛型可以减少重复的代码。

常见的用法就是用泛型来限制集合存储元素的类型,这样编译器可以进行静态类型检查,从数组中取出的对象也不再是Dart中应该是Dynamic或者Object类型,类似于OC里的id类型:

var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
names.add(42); // 错误

另外一个使用泛型的原因是减少重复的代码。 泛型可以在多种类型之间定义同一个实现, 同时还可以继续使用检查模式和静态分析工具提供的代码分析功能。 例如,假设你创建了一个用于缓存对象的接口:

abstract class ObjectCache {
  Object getByKey(String key);
  void setByKey(String key, Object value);
}

后来发现需要一个相同功能的字符串类型接口,因此又创建了另一个接口:

abstract class StringCache {
  String getByKey(String key);
  void setByKey(String key, String value);
}

后来,又发现需要一个相同功能的数字类型接口 … 这里你应该明白了。
泛型可以省去创建所有这些接口的麻烦。 通过创建一个带有泛型参数的接口,来代替上述接口:

abstract class Cache<T> {
  T getByKey(String key);
  void setByKey(String key, T value);
}

泛型类型的构造函数

稍微回顾一下Swift中使用泛型创建一个数组:

var shoppingList: [String] = ["Eggs", "Milk"]

Dart中的写法:

var names = <String>['Seth', 'Kathy', 'Lars'];
 List<String> names1 = ['Seth', 'Kathy', 'Lars'];

运行时中的泛型集合

Dart 中泛型类型是 固化的,也就是说它们在运行时是携带着类型信息的。 例如, 在运行时检测集合的类型:

var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
print(names is List<String>); // true

提示: 相反,Java中的泛型会被 擦除 ,也就是说在运行时泛型类型参数的信息是不存在的。 在Java中,可以测试对象是否为 List 类型, 但无法测试它是否为 List<String> 。

限制泛型类型

使用泛型类型的时候,可以使用 extends 实现参数类型的限制,参数必须是extends类或其子类类型。

class Location<T extends num> {
  T x;
  T y;

  Location(this.x, this.y);
}
main(List<String> args) {
  Location l2 = Location<int>(10, 20);
  print(l2.x.runtimeType);
    
  // 错误的写法, 类型必须继承自num
  Location l3 = Location<String>('aaa', 'bbb');
}

使用泛型函数

最初,Dart仅仅在类中支持泛型。后来一种称为泛型方法的新语法允许在方法和函数中使用类型参数。具体用法到时候在网上再查查,这玩意儿对我来说就是个新鲜事物。

main(List<String> args) {
  var names = ['why', 'kobe'];
  var first = getFirst(names);
  print('$first ${first.runtimeType}'); // why String
}

T getFirst<T>(List<T> ts) {
  T tmp = ts[0];
  return tmp;
}

这里的 first (<T>) 泛型可以在如下地方使用参数 T :

  • 函数的返回值类型 (T).
  • 参数的类型 (List<T>).
  • 局部变量的类型 (T tmp).

库的使用

Dart中任何一个dart文件都是一个库,即使你没有用关键字library声明。import语句用来导入一个库,后面跟一个字符串形式的Uri来指定表示要引用的库,语法如下:

import 'dart:html';

使用相对路径导入的库,通常指自己项目中定义的其他dart文件:

import 'lib/student/student.dart';

Pub包管理工具管理的一些库,包括自己的配置以及一些第三方的库,通常使用前缀package:

//Pub包管理系统中有很多功能强大、实用的库,可以使用前缀 package:
import 'package:flutter/material.dart';

库文件中内容的显示和隐藏
如果希望只导入库中某些内容,或者刻意隐藏库里面某些内容,可以使用show和hide关键字:
show关键字:可以显示某个成员(屏蔽其他)
hide关键字:可以隐藏某个成员(显示其他)

import 'lib/student/student.dart' show Student, Person;

import 'lib/student/student.dart' hide Person;

当库中内容和当前文件中的名字冲突的时候,可以使用as关键字来使用命名空间:

import 'lib/student/student.dart' as Stu;

Stu.Student s = Stu.Student();

库的定义

library关键字
通常在定义库时,我们可以使用library关键字给库起一个名字:

library math;

export关键字
将不同的库分散在不同的dart文件中,通过export关键字暴露给外界使用:

library utils;

export "mathUtils.dart";
export "dateUtils.dart";

//使用时
//import "lib/utils.dart";

Dart主要语法基本告一段落了,现在去项目里看代码的时候应该会好很多了。
参考资料:
coderwhy-Flutter之搞定Dart系列
Dart编程语言中文网

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容