Flutter开发指南之理论篇:Dart语法04(库,异步,正则表达式)

总目录

Flutter开发指南之理论篇:Dart语法01(数据类型,变量,函数)
Flutter开发指南之理论篇:Dart语法02(运算符,循环,异常)
Flutter开发指南之理论篇:Dart语法03(类,泛型)
Flutter开发指南之理论篇:Dart语法04(库,异步,正则表达式
Flutter开发指南之理论篇:Dart语法05(消息循环模型)


 Dart是一门面向对象语言,它针对web 和移动设备开发进行了优化,主要特点为:

  • 一切皆对象!无论是数字,函数还是null,所有对象继承自Object类;
  • 声明一个变量时可以不指定具体类型,Dart可以自动推断类型;
  • Dart支持顶层函数,函数是一等对象,且函数可作为参数传递;
  • Dart使用_开头表示私有属性,没有关键字publicprotectedprivate

1. 库

 库是共享代码的集合,由多个dart源文件组成,它不仅提供了API,而且对代码起到了封装的作用,有利于代码模块化。Dart程序依赖于库,Dart内置了一些常用的库,我们也可以自定义库或者通过packages来导入一些第三方库。

1.1 使用库

 Dart程序中使用import导入一个库,主要有以下几种形式:

(1)一般导入

 Dart中的库主要来源三个方面:Dart内置库第三方库本地文件

// a. 引入dart语言内置库
// 引入内置库时,在使用的uri中以dart:开头
// 格式:'dart:库名'
import 'dart:core' // core库,内置类型,集合和其它核心功能
import 'dart:math' // math库,数学常数,函数以及随机数生成器

// b. 导入第三方库
// 在引用包管理器提供的库时,uri中以package开头
// 格式:'package:指向库的URI'
import 'package:flutter/material.dart';
import 'package:css_colors/css_colors.dart';

// c. 导入本地文件
// 引用本地文件时,uri字符串中直接填写文件的相对路径
import './tools/network.dart';

(2)指定库的前缀

 如果导入两个存在冲突标识符的库,则可以为这两个库,或者其中一个指定前缀。

import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;

// 使用 lib1 中的 Element。
Element element1 = Element();

// 使用 lib2 中的 Element。
lib2.Element element2 = lib2.Element();

(3)导入库的一部分

 如果你只使用库的一部分功能,则可以选择需要导入的内容。

// 只导入lib1.dart的foo
import 'package:lib1/lib1.dart' show foo;

// 导入lib1.dart所有除foo
import 'package:lib2/lib2.dart' hide foo;

(4)延迟加载库

 延迟加载库(Deferred loading),Dart支持在应用需要时才加载库。常用的使用场景:

  • 减少APP的启动时间;
  • 执行A/B测试,比如尝试各种方案的实现;
  • 加载很少使用的功能。

 首先,使用deferred as导入库。

// 导入延迟加载的库,且将库名命名为hello
import 'package:greetings/hello.dart' deferred as hello;

 其次,需要时使用loadLibrary()函数来加载库。

// async表示greet()函数为异步函数
// await会等待库被异步加载完毕,才会执行下一条语句
Future greet() async {
  await hello.loadLibrary();
  hello.printGreeting();
}

 注意:

  • a. loadLibrary()函数无论被调用几次,该库只是载入一次且返回一个Future;
  • b. 延迟加载库中的常量,只有在库加载完毕后才可以使用;
  • c. 在库被加载完毕之前,库中的类型不可用。如果需要使用延迟库中的类型,可以考虑把接口类型移动到另外一个库中,让两个库分别导入这个接口库;

1.2 常见库

 Dart语言内置了一些常用的库,它们以"import dart:库名;"的形式导入到程序中。

描述
dart:core内置类型,集合,正则以及其他核心功能。 该库会被自动导入每个 Dart 程序。
dart:async支持异步编程,包括Future和Stream等类。
dart:math数学常数和函数,以及随机数生成器。
dart:ioIO操作,比如文件处理,Sockets编程,HTTP等。
dart:convert用于在不同数据表示之间进行转换的编码器和解码器,包括 JSON 和 UTF-8
dart:html操作DOM中的对象和元素,访问HTML5 API等。

2. 异步

 Dart中内置了dart:async库,用于支持异步处理耗时任务,如网络请求,IO操作等。该库包括FutureStream等类,以及返回Future或Stream对象的函数,这些函数被称为异步函数。在Dart中,还支持使用asyncawait关键字允许我们以同步的形式写异步的代码。

2.1 Future

Future表示一个异步操作的最终结果以及结果值表示,一个Future只会对应一个结果,即成功或失败。换句话说,Future就是用于处理异步操作的,表示在未来会返回某个值,并可以使用then()方法catchError()方法来注册监听Future的处理结果。Future的所有API均返回Future对象,因此支持链式调用,常见示例:

// getFuture()为伪代码
// 用于返回一个Future对象
Future<int> future = getFuture();
future.then((value) => handleResult(value));     
      .catchError((error) => handleError(error));

2.1.1 Future用法

(1)创建Future

  • Future(FutureOr<T> computation())

 Future构造方法,用于创建一个基本的Future。示例代码:

var future = Future(() {
   print('今天天气不错'); 
});
print('6666666');

// 打印结果:
//          6666666
//          今天天气不错

// var future = await Future(...)

 或许你会问为什么"今天天气不错"会在后面输出,原因是future默认异步执行的,这或许跟Dart的事件消息循环模型有关,我们下篇重点讨论。当然,如果你希望上述代码能够按顺序打印,可以在future前面添加await关键字,即await会等待直到future执行后,才继续执行后面的代码。

  • Future.delayed(Duration duration, [FutureOr<T> computation()])

 Future构造方法,用于延迟创建一个基本的Future。示例代码:

// 延迟5后打印字符串
// 并创建一个返回值为"hello world"的Future对象
var futureDelayed = Future<String>.delayed(Duration(seconds: 5), () {
            print('今天天气不错');
            return 'hello world';
        });
  • Future.value([FutureOr<T>? value])

 Future构造方法,用于创建一个返回指定value值的Future。示例代码:

var future = Future.value('hello world.');
print(future);

// 打印结果:
//      Instance of 'Future<String>'
  • Future.error(Object error, [StackTrace? stackTrace])

 Future构造方法,用于创建一个Future对象,以错误状态完成。示例代码:

Future.error('The state is illegal.');
  • Future.microtask(FutureOr<T> computation())

 Future构造方法,用于创建属于microtask queue的Future对象。示例代码:

Future((){
  print("Future event 1");
});
Future((){
  print("Future event 2");
});
Future.microtask((){
 print("microtask event");
});

//打印结果:
//      microtask event
//      Future event 1
//      Future event 2

 Dart的消息循环模型中有两个异步任务队列,即event queuemicrotask queue,其中,microtask queue优先级更高,而future的任务默认是属于event queue。Future.microtask的作用就是创建一个属于microtask queue的Future,因此,该Future会先执行。

(2)处理Future结果

  • then<R>(FutureOr<R> onValue(T value), {Function? onError}) → Future<R>

 Future成员方法,用于在Future异步完成的时候获取Future结果。示例代码:

Future.value('Hello')
      .then((value) {
          return Future.value('$value China!');
      }).then((value) {
          return Future.value('$value, Your Chang\'e5 does a gread Job!');
      }).then((value) {
          print('$value');
      })

// 打印结果:
//          Hello China!Your Chang'e5 does a gread Job!
  • catchError(Function onError, {bool test(Object error)}) → Future<T>

 Future成员方法,如果异步任务发生错误,我们可以在catchError中捕获错误。示例代码:

Future.delayed(new Duration(seconds: 2),(){
   //return "hi world!";
   throw AssertionError("Error");  
}).then((data){
   //执行成功会走到这里  
   print("success");
}).catchError((e){
   //执行失败会走到这里  
   print(e);
});

 当异步任务中抛出一个异常,then的回调函数不会被调用,取而代之的是catchError的回调函数被调用。除了使用catchError捕获异步任务的错误,then方法还有一个可选参数onError来捕获异常。因此,上述代码等价于:

Future.delayed(new Duration(seconds: 2),(){
   //return "hi world!";
   throw AssertionError("Error");  
}).then((data){
   //执行成功会走到这里  
   print("success");
}, onError: (e) {
    //执行失败会走到这里  
    print(e);
});
  • whenComplete(FutureOr<void> action()) → Future<T>

 Future成员方法,如果异步任务是成功还是失败,都会执行whenComplete的回调函数。示例代码:

Future.delayed(new Duration(seconds: 2),(){
   //return "hi world!";
   throw AssertionError("Error");
}).then((data){
   //执行成功会走到这里 
   print(data);
}).catchError((e){
   //执行失败会走到这里   
   print(e);
}).whenComplete((){
   //无论成功或失败都会走到这里
});

(3)高级用法

  • wait<T>(Iterable<Future<T>> futures, ...)

 Future静态成员方法,用于等待多个Future完成,并收集它们的结果。需要注意的是,假如所有的Future都能正常结果返回,则Future的返回结果是所有指定Future的结果集合;假如其中一个Future有error返回,则Future的返回结果是第一个error的值。示例代码:

Future.wait([
  // 创建Future对象1
  // 2秒后返回结果  
  Future.delayed(new Duration(seconds: 2), () {
    return "hello";
  }),
  // 创建Future对象2
  // 4秒后返回结果  
  Future.delayed(new Duration(seconds: 4), () {
    return " world";
  })
]).then((results){
  print(results[0]+results[1]);
}).catchError((e){
  print(e);
});

// 4s秒后打印结果:
//      "hello world"
  • forEach<T>(Iterable<T> elements, FutureOr action(T element))

 Future静态成员方法,用于根据某个集合创建一系列的Future,并按顺序执行这些Future。示例代码:

// 根据集合{1,2,3}
// 创建3个延迟对应秒数的Future
Future.forEach({1,2,3}, (num){
  return Future.delayed(Duration(seconds: num), (){
    print(num);
  });
});

// 执行结果:
//      1秒后,打印1
//      2秒后,打印2
//      3秒后,打印3
  • doWhile(FutureOr<bool> action())

 Future静态成员方法,用于重复性地执行某一个动作,直到返回false或者Future(其结果返回false),则退出循环。示例代码:

// 随机的延迟打印一个数
// 当随机数为5时,返回false
Future.doWhile(() {
    var value = Random().nextInt(10);
    if (value == 5) {
        print("game over, value = $value")
        // 返回false,循环退出
        return false;
    }
    return Future.delayed(Duration(seconds: value), () {
        print('current value = $value');
        // 该Future返回true
        // 不会使循环退出
        return true;
    });
}).then(print)
  .catchError(print);
  
// 执行结果(随机):
//      当value=2时,2秒后,打印current value = 2
//      当value=8时,8秒后,打印current value = 8
//      当value=1时,1秒后,打印current value = 1
//      当value=5时,打印game over, value = 2

2.1.2 async和await

 在生产过程中,异步任务的嵌套调用还是比较常见的,即某个异步任务依赖于其他异步任务的结果。如果在开发中,处理不好这种大量异步任务依赖的情况,就会导致代码可读性下降或者出错率提高,最终造成代码难以维护,这种问题又被称为“回调地狱(Callback Hell)”。示例代码:

// 1. 定义异步任务

Future<String> login(String userName, String pwd) {...};
Future<String> getUserInfo(String id) {...};
Future saveUserInfo(String userInfo){...};

// 2. 业务使用场景:
//      先登录,拿到ID获取用户信息userInfo,再将用户缓存到本地

login("jiangdg","66668888").then((id) {
    getUserInfo(id).then((userInfo) {
        saveUserInfo(userInfo).then(() {
            ...
        });
  });
})

 在Dart中,为了解决“回调地狱”的问题,就提供了asyncawait方式使用Future,让我们能够使用同步的形式写出异步代码。其中,await只能在async函数中出现,被async标识的函数又被称为异步函数,该函数返回一个Future对象(注:如果函数没有返回Future对象,Dart会自动将函数的结果包装成Future对象)。await会一直等待async函数返回的Future结果,才会执行下一步的代码。优化上述代码如下:

// 异步函数
saveUserInfoTask() async {
    try {
        var id = await login("jiangdg","66668888");
        var userInfo = await getUserInfo(id);
        await saveUserInfo(userInfo);
        ...
        // 执行接下来的代码
    } catch(e) {
        // 捕获异步任务错误
        print(e)
    }
}

 需要注意的是,async/await方式使用try/catch机制来处理异常。实际上,async/await都只是Dart一个语法糖,编译器或解释器最终都会将其转化为一个Future的链式调用。因此,上述代码也可以直接使用Future的API实现,但是比起async/await方式仍然有一层回调。代码如下:

login("jiangdg","66668888").then((id) {
        return getUserInfo(id);
    }).then((userInfo) {
        return saveUserInfo(userInfo);
    }).then(() {
        ...
        //执行接下来的操作 
    }).catchError((e){
        //错误处理  
        print(e);
    });

2.2 Stream

 和Future只能接收一个异步任务结果不同,Stream可以接收多个异步操作的结果,无论是成功还是失败(异常)。因此,Stream常用于多次读写数据的异步任务场景,如网络下载文件,文件读写等。示例代码:

Stream.fromFutures([
  // 1秒后返回结果
  Future.delayed(new Duration(seconds: 1), () {
    return "hello 1";
  }),
  // 抛出一个异常
  Future.delayed(new Duration(seconds: 2),(){
    throw AssertionError("Error");
  }),
  // 3秒后返回结果
  Future.delayed(new Duration(seconds: 3), () {
    return "hello 3";
  })
]).listen((data){
   print(data);
}, onError: (e){
   print(e.message);
},onDone: (){

});

// 打印结果:
//          hello 1
//          Error
//          hello 3

3. 正则表达式

正则表达式(Regular Expression)描述了一种字符串匹配的模式(pattern),可以用来检查一个字符串是否含有某种字串,或者将匹配的子串替换,或者从某个字符串中取出符合某个条件的自创等。正则表达式由普通字符(例如字符a到z)以及特殊字符(称为"元字符")组成的文字模式,它作为一个模板,将某个字符模式与所搜索的字符串进行匹配。

3.1 语法规则

3.1.1 普通字符

 普通字符包括所有大写和小写字符,所有数字,标点符号和一些其它符号。

字符描述
[…]匹配[…]中所有字符,如[jdg]能够匹配字符串“jiangdongguo”中所有的j d g字母
[^…]匹配除了[…]中所有字符
[A-Z][A-Z]表示一个区间,即匹配所有大写字符
[a-z][a-z]表示一个区间,即匹配所有小写字符
[\s\S]匹配所有。\s 是匹配所有空白符,包括换行,\S 非空白符,包括换行。
\w匹配字母、数字、下划线。等价于 [A-Za-z0-9_]
\W匹配非字母、数字、下划线。等价于 ‘[^A-Za-z0-9_]’
\d匹配一个数字字符。等价于 [0-9]
\D匹配一个非数字字符。等价于 [^0-9]

3.1.2 特殊字符

 所谓特殊字符,就是一些有特殊含义的字符。若要匹配这些特殊字符,必须首先使字符"转义",即,将反斜杠字符\放在它们前面,比如love\*China匹配love*China。

字符描述
.匹配除换行符(\n,\r)之外的任何单个字符
[标记一个中括号表达式的开始
{标记限定符表达式的开始

3.1.3 限定符

 限定符用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。

字符描述
*匹配前面的子表达式零次或多次
+匹配前面的子表达式一次或多次
?匹配前面的子表达式零次或一次
{n}n 是一个非负整数。匹配确定的 n 次
{n,}n 是一个非负整数。至少匹配n 次
{n,m}m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次

 注意:* + ?属于限定符,也属于特殊字符,如果要匹配它们,需要\转义。

3.1.4 定位符

 定位符用来描述字符串或单词的边界,^$分别指字符串的开始与结束,\b描述单词的前或后边界,\B表示非单词边界。

字符描述
^匹配输入字符串的开始位置,如果在[]表达式使用,表示取反,如[^…]
$匹配输入字符串的结尾位置
\b匹配一个单词边界,即字与空格间的位置
\B非单词边界匹配置

 注意:^ $属于定位符,也属于特殊字符,如果要匹配它们,需要\转义。另外,不能将限定符与定位符一起使用。

3.1.5 选择符

 用圆括号()将所有选择项括起来,相邻的选择项之间用|分隔。() 表示捕获分组,()会把每个分组里的匹配的值保存起来,即()匹配得到的是一组结果。

字符描述
( )标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。
|指明两项之间的一个选择

 注意:^ $属于选择符,也属于特殊字符,如果要匹配它们,需要\转义

3.2 RegExp类

 Dart内置库dart:core中提供了RegExp类和Match类,支持使用正则表达式对字符串进行高效搜索和模式模式匹配。其中,RegExp类表示一个正则表达式对象,Match 类提供对正则表达式匹配对象的访问。示例代码:

// 案例1
// 判断电话号码,11位
bool isPhone(String input) {
    var pattern = new RegExp(r"1[0-9]\d{9}$");
    return pattern.hasMatch(input);
}

// 案例2
// 登录密码,6~16位数字和字符组合
bool isLoginPassword(String input) {
    var pattern = RegExp(r"(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,16}$");
    return pattern.hasMatch(input);
}

// 案例3
extraURLInfo() {
    String url = "http://www.runoob.com:80/html/html-tutorial.html";
    RegExp pattern = RegExp(r"(\w+):\/\/([^/:]+)(:\d*)?([^# ]*)");
    Iterable<Match> matches = pattern.allMatches(url);
    for (Match m in matches) {
      String match = m.group(0);
      print(match);
    }
}

 案例3依次打印:

http://www.runoob.com:80/html/html-tutorial.html
http  
www.runoob.com  
:80  
/html/html-tutorial.html

参考文献:

1. Dart编程语言中文网
2. Dart API说明
3. Flutter开发实战

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 黑客帝国 设计师:白松林 返回首页