和JavaScript,swift中一样,可以接受任何类型的变量,但是在Dart语言中一旦赋值,则不能不能改变其类型.
vat temp;
temp = 'hello word';
// 下面的代码在Dart中会报错,因为变量temp的类型已经确定是String 类型,所以不能赋值 int类型的数据,类型一旦确定后不能再更改.(Dart中也是有类型推断的,和swift中一样,可以推断其数据的类型)
temp = 1000;
Object: 所有对象的基类(包括Function和null),声明对象后期可以随意改变类型.
Object temp;
temp = 1000;
//下面这句代码是正确的
temp = 'hello word';
dynamic: 在声明对象方面和Object想同.
不同点:dynamic对象和OC重点 id很相似,编译器会提供所有的可能组合,但是Onject只能调用Object的方法(否者变异其会报错)
dynamic a;
Object b;
main() {
a = "";
b = "";
printLengths();
}
printLengths() {
// no warning
print(a.length);
// warning:
// The getter 'length' is not defined for the class 'Object'
print(b.length);
}
变量a不会报错, 变量b编译器会报错
final:在第一次使用时被初始化.
const: 编译器常量,在编译期就要赋值
被final和const修饰的变量,类型可以省略
//可以省略String这个类型声明
final str = "hi world";
//final String str = "hi world";
const str1 = "hi world";
//const String str1 = "hi world";
1.函数的声明:
//返回值 函数名称 (参数列表) {函数体} 声明时如果没有返回类型,会被当做 是dynamic类型
bool isNoble(int atomicNumber) {}
注意:函数返回值没有类型推断
2.对于只含有一个表达式的函数,可以简写:
void main() => runApp(MyApp());
//和下面这种方式是等价的
void main() {
return runApp(MyApp());
}
3.函数作为变量:
var say = (str){
print(str);
};
say("hi world");
4.函数作为参数传递:
void execute(var callback) {
callback();
}
execute(() => print("xxx"))
5.函数的可选位置参数
//[String device] 表示可选位置参数 ,传值时可传可不传
String say(String from, String msg, [String device]) {}
6.可选命名参数
//设置[bold]和[hidden]标志
void enableFlags({bool bold, bool hidden}) {
//调用函数时,可以指定参数名
enableFlags(bold: true, hidden: false);
}
Dart类库有非常多的返回Future或者Stream对象的函数。 这些函数被称为异步函数:它们只会在设置好一些耗时操作之后返回,比如像 IO操作。而不是等到这个操作完成。
async和await关键词支持了异步编程,允许您写出和同步代码很像的异步代码。
用来处理异步操作,异步处理成功了就执行成功的操作,异步处理失败了就捕获错误或者停止后续操作。一个Future只会对应一个结果,要么成功,要么失败(Future所有的API返回值都是一个Future对象,方便你链式调用)
我们使用Future.delayed来创建一个延时任务(模拟一个网络请求数据的过程),2秒后在then()中接受返回的结果并且打印结果.
Future.delayed(new Duration(seconds: 2),(){
return "hi world!";
}).then((data){
print(data);
});
我们在异步任务中抛出了一个异常,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);
});
有些时候,我们会遇到无论异步任务执行成功或失败都需要做一些事的场景,比如在网络请求前弹出加载对话框,在请求结束后关闭对话框。这种场景,有两种方法,第一种是分别在then或catch中关闭一下对话框,第二种就是使用Future的whenComplete回调,我们将上面示例改一下:
Future.delayed(new Duration(seconds: 2),(){
//return "hi world!";
throw AssertionError("Error");
}).then((data){
//执行成功会走到这里
print(data);
}).catchError((e){
//执行失败会走到这里
print(e);
}).whenComplete((){
//无论成功或失败都会走到这里
});
有时候我们需要同时等待几个异步任务完成后,才能做一些操作,我们可以使用Future.wait,优点类似于这个swift中的group(GCD的线程组).
但是只有wait中所有的Future都执行成功,才会调用.then()语句,否则执行.catchError()中的语句
Future.wait([
// 2秒后返回结果
Future.delayed(new Duration(seconds: 2), () {
return "hello";
}),
// 4秒后返回结果
Future.delayed(new Duration(seconds: 4), () {
return " world";
})
]).then((results){
print(results[0]+results[1]);
}).catchError((e){
print(e);
});
如果代码中有大量异步逻辑,并且出现大量异步任务依赖其它异步任务的结果时,必然会出现Future.then回调中套回调情况。举个例子,比如现在有个需求场景是用户先登录,登录成功后会获得用户ID,然后通过用户ID,再去请求用户个人信息,获取到用户个人信息后,为了使用方便,我们需要将其缓存在本地文件系统,代码如下:
//先分别定义各个异步任务
Future<String> login(String userName, String pwd){
...
//用户登录
};
Future<String> getUserInfo(String id){
...
//获取用户信息
};
Future saveUserInfo(String userInfo){
...
// 保存用户信息
};
接下来,执行整个任务流:
login("alice","******").then((id){
//登录成功后通过,id获取用户信息
getUserInfo(id).then((userInfo){
//获取用户信息后保存
saveUserInfo(userInfo).then((){
//保存用户信息,接下来执行其它操作
...
});
});
})
可以感受一下,如果业务逻辑中有大量异步依赖的情况,将会出现上面这种在回调里面套回调的情况,过多的嵌套会导致的代码可读性下降以及出错率提高,并且非常难维护,这个问题被形象的称为回调地狱(Callback Hell)。回调地狱问题在之前JavaScript中非常突出,也是JavaScript被吐槽最多的点,但随着ECMAScript6和ECMAScript7标准发布后,这个问题得到了非常好的解决,而解决回调地狱的两大神器正是ECMAScript6引入了Promise,以及ECMAScript7中引入的async/await。 而在Dart中几乎是完全平移了JavaScript中的这两者:Future相当于Promise,而async/await连名字都没改。接下来我们看看通过Future和async/await如何消除上面示例中的嵌套问题。
login("alice","******").then((id){
return getUserInfo(id);
}).then((userInfo){
return saveUserInfo(userInfo);
}).then((e){
//执行接下来的操作
}).catchError((e){
//错误处理
print(e);
});
正如上文所述, “Future 的所有API的返回值仍然是一个Future对象,所以可以很方便的进行链式调用” ,如果在then中返回的是一个Future的话,该future会执行,执行结束后会触发后面的then回调,这样依次向下,就避免了层层嵌套。
通过Future回调中再返回Future的方式虽然能避免层层嵌套,但是还是有一层回调,有没有一种方式能够让我们可以像写同步代码那样来执行异步任务而不使用回调的方式?答案是肯定的,这就要使用async/await了,下面我们先直接看代码,然后再解释,代码如下:
task() async {
try{
String id = await login("alice","******");
String userInfo = await getUserInfo(id);
await saveUserInfo(userInfo);
//执行接下来的操作
} catch(e){
//错误处理
print(e);
}
}
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: (){
});
上面的代码依次会输出:
I/flutter (17666): hello 1
I/flutter (17666): Error
I/flutter (17666): hello 3
本文参考链接: https://book.flutterchina.club/chapter1/dart.html