异步编程
Dart语言和JavaScript语言的共同点是单线程,同步代码会阻塞程序,因此程序里能看到大量的异步操作,它是用Future对象来执行相关操作的,并且async函数使用await关键字时才被执行,直到一个Future操作完成,Future支持链式操作的方式,可以按顺序执行异步操作。
Future是什么?
一个Future是一个Future自身的泛型Flutter对象,它表示一个异步操作产生的T类型的结果,如果结果的值不可用,Future的类型会是Future,如果返回一个Future的函数被调用了,将会发生以下两件事:
(1)这个函数加入待完成的队列并且返回一个未完成的Future对象
(2)当这个操作结束了,Future对象返回一个值或者错误。
例如:
Future<int> future=getFuture(); future.then((value)=>handleValue(value)) .catchError((error)=>handleError(error)) .whenComplete()=>handlerComplete();
如果你是前端的开发人员,可以理解Future类似前端里的Promise。我们在future.then中接收异步处理结果,并根据业务需求做相应的处理。而future.catchError则用于在异步函数中捕获并处理错误。在有些业务场景下,无论是异步任务的处理结果是成功还是失败,我们都需要再进行一些处理,这时候可以使用Future的whenComplete进行回调。
async和await
当遇到需要延迟的运算(async)时,将其放到延迟运算的队列(await)中去,把不需要延迟的运算的部分先执行完,最后在来处理延迟的运算部分。但是,要使用await,就必须在有async标记的函数中运行,否则这个await会报错。
其实这个和JavaScript也很像,async和await是Future的语法糖(Syntacticsugar),解决了回调地狱(Callback Hell)的问题。
那么回调地狱又是什么?这个需要结合具体业务场景来说,比如“业务1->业务2->业务3”,这三个任务都是异步的,那么业务1成功之后调用业务1的then回调,然后业务1的then里面在调用业务2,依次类推,就形成了回调地狱。回调地狱多了之后,我们的代码会非常难看,因为需要大量的缩进,比如这种:
step1('step1').then(step1Result(){ step2('step2').then(step2Result(){ step3('step3').then(step3Result(){ //依次类推456.... }) }) })
我们刚在上面讲解了async和await是Future的语法糖,我们在使用过程中给人的感觉是在调用同步的代码。为了解决这种回调地狱,我们对以上代码结构进行了一些调整,修改后的代码如下:
steps() async{ try{ String step1Result=await step1('step1'); String step2Result=await step2(step1Result); String step3Result=await step3(step2Result); //456...... }catch(e){ print(e); } }
从上面的代码我们可以看到,await必须被包裹在async里面,如果没有async就会报错。后续我们将通过其他章节在进行详细的使用。
继承,接口实现
在Dart语言中,继承和接口实现基本上与Java类似,但是我们需要注意以下几点:
(1)继承:构造函数不能被继承,而且在Dart语言中,没有Java中的公有与私有的修饰符,因此,可以直接访问超类中的所有变量与方法。
(2)接口实现:在Dart语言中,没有接口的修饰符,但是每个类都是一个隐式的接口,这个接口包含类里的所有成员变量与方法,当类被当作接口使用的时候,类中的方法就是接口中的方法,它需要在子类里重新被实现,而且实现的时候需要加上@override关键字。
使用方式如下:
abstract class A{ void printA(); } class B{ void printB(){ } } class C{ void printC(){ print('C'); } } class SoftwareSngineer extends C implements A,B{ @override void printA{ //重写 } @override void printB(){ //重写 } }
混合
在Dart语言中,我们有一个新的特性,那就是混合的语言特性mixins,它的作用是在类中混入其他功能,mixins最早出现在Lisp语言中。你可以这么来理解,就是在面向对象的语言中,mixins是一个可以把自己的方法提供给其他类使用,但却不需要成为其他类的父类的类,它以非继承的方式来复用类中的代码。要使用mixins,则要用关键字with来复用类中的代码,使用方式如下:
abstract class A{ factory A._(){ return null; } void printA(){ print('A'); } } class C with A{ @override void printA(){ print('A'); } } void main(){ C()..printA(); }
那我们现在就有一个疑问了,如果同时使用继承,接口实现与混合,并且@override的方法都一样,那么执行的优先级会怎么样?我们直接上代码跑一下就清楚了:
class Cat{ void show(){ print('小猫'); } } class Brid{ void show(){ print('小鸟'); } } class Owner{ void show(){ print('主人'); } } class person1 extends Owner with Cat,Brid{ void show(){ print('主人1养了猫和鸟'); } } class person2 extends Owner with Cat implements Brid{ void show(){ print('主人2养了猫和鸟'); } } void main(){ person1()..show(); person2()..show(); }
如果代码如上,没有任何注释,那么会直接执行类本身的方法,输出效果如下:
现在我们注释调用两个person中的方法,其他代码不变:
class person1 extends Owner with Cat,Brid{ //void show(){ //print('主人1养了猫和鸟'); //} } class person2 extends Owner with Cat implements Brid{ //void show(){ //print('主人2养了猫和鸟'); //} }
那么现在打印出来的效果就是这样:
从这里我们可以看出来,假如使用了混合,继承与接口实现,那么它的执行优先级顺序是mixins->extends->implements。
泛型
在Dart语言中,泛型与Java中很相似,比如List。用尖括号括起来的就是泛型的写法。在List中,这个E代表泛型的类型,且不一定要用E表示,还可以用T,S,K等表示,我们先来简单的使用一下:
void main(){ List animals=new List<String>(); animals.addAll('小猫','小狗','小兔'); }
在上面代码中,我们使用了List集合来存储小动物,并且指定了List泛型,这样定义就说说明只能存储字符串类型。
Dart语言基础就这么多,重点都在后面,前面大部分只要学过其他的语言,看看根本不费劲,Dart语言是Flutter开发的主要语言,必须要掌握的哦。