04-Dart語(yǔ)法--異步編程

const與final

  1. 區(qū)別一:final要求變量只能初始化一次,并不要求賦的值一定是編譯時(shí)常量。而const要求在聲明時(shí)初始化,并且賦值必需為編譯時(shí)常量。
  2. 區(qū)別二:final是惰性初始化,即在運(yùn)行時(shí)第一次使用前才初始化。而 const 是在編譯時(shí)就確定值了。
//這里 const 和 final 沒有區(qū)別,因?yàn)樽兞?a 被立即賦值為一個(gè)數(shù)字常量
void test1() {
  var a1 = 1;
  const a2 = 1;
  final a3 = 1;

  int a4 = 1;
  const int a5 = 1;
  final int a6 = 1;
}

什么是編譯時(shí)常量

指的是:字面量(如數(shù)字、bool、字符串、List的字面量形式)、其它常量或者常量的算術(shù)運(yùn)算,也可以是這些的組合形式,簡(jiǎn)單地說(shuō)常量就是可以在編譯時(shí)確定的值。如下:

//const可以如下定義
void test2() {
  const a = 8;
  const b = false;
  const c = a;
  const d = 5 * 3;
  const e = a * d + 2;
}

而其它形式,比如實(shí)例化一個(gè)新的對(duì)象,一個(gè)函數(shù)調(diào)用的返回值就不是編譯時(shí)常量了,它們?cè)谶\(yùn)行時(shí)才能獲得結(jié)果。

void test3() {
  final x1 = new DateTime.now(); //ok
  const x2 = new DateTime.now();//error
}

final 實(shí)例成員

//聲明時(shí)直接初始化
class X1 {
  final a = 0;
}
//或構(gòu)造方法中初始化
class X2 {
  final a;
  X2(this.a){
  }
}
//或初始化列表中初始化
class X3 {
  final a;
  X3(a): this.a = a{
  }
}

const 實(shí)例成員

class X {
  //只能聲明時(shí)直接初始化
  static const a = 0;//必須是static的
  X(){
  }
}

常量對(duì)象和常量構(gòu)造函數(shù)

前面說(shuō)的編譯時(shí)常量還包括常量對(duì)象,也就是通過(guò) const 構(gòu)造函數(shù)創(chuàng)建的對(duì)象。

  1. 所有實(shí)例成員都是 final 或 const 實(shí)例成員,那么該類所創(chuàng)建的對(duì)象的狀態(tài)就是不可變的。
  2. 定義 const 構(gòu)造函數(shù),const 構(gòu)造函數(shù)所創(chuàng)建的對(duì)象就是常量對(duì)象。
  3. 用 const 代替 new 來(lái)調(diào)用構(gòu)造函數(shù)。
  4. 可以用 new 調(diào)用 const 構(gòu)造函數(shù),但那樣就不能用 const 聲明該變量了。
  5. const 構(gòu)造函數(shù)也不能有函數(shù)體。
  6. 常量對(duì)象還有一個(gè)特點(diǎn),相同的常量對(duì)象始終只有一個(gè)。
class Constant {
  static const a = 1;
  final b;

  const Constant(this.b); //不能有函數(shù)體

  static const m = const Constant(3);
}

main(){
  const x1 = const Constant(3);// ok
  const x2 = new Constant(3);// error
  final x3 = const Constant(3);// ok
  final x4 = new Constant(5);// ok
  
  assert(x1 == x3);
  assert(x1 != x4);
}

區(qū)別三:const 可以修飾變量,也可以修飾值,而 final 只用來(lái)修飾變量。

void test4(){
  const list1 = [1, 2, 3]; // error
  const list2 = const [1, 2, 3];
  const list3 = const [new DateTime.now(), 2, 3]; // error, 因?yàn)閚ew DateTime.now()不是const
  const list4 = const [const Constant(3), 2, 3];

  var x1 = 5;
  const list5 = const[x1]; //error,x1不是const
  
  const x2 = 5;
  const list6 = const[x2];

  final list7 = [1, 2, 3];
  final list8 = const [1, 2, 3];
  final list9 = const [new DateTime.now(), 2, 3];// error, 因?yàn)閚ew DateTime.now()不是const
}

const 修飾值的時(shí)候,要求值必需是常量或由常量組成。var、final等在左邊定義變量時(shí),并不關(guān)心右邊是不是常量。但如果右邊用了const,那么不管左邊是什么要求,右邊都必需是常量。

異步

Async和await

Future getName1() async {
  await getStr1();
  print('getName1');
}

getStr1() {
  print('getStr1');
}

getName2() {
  print('getName2');
}

getName3() {
  print('getName3');
}

goAsync() {
  getName1();
  getName2();
  getName3();
}
//輸出結(jié)果
getStr1
getName2
getName3
getName1
  1. return的類型為T,那么函數(shù)的返回類型應(yīng)該為Future<T>,或T的父類,否則會(huì)產(chǎn)生靜態(tài)警告。
  2. 如果函數(shù)中return的類型是Future<T>,那么函數(shù)的返回類型同樣為Future<T>,而不是Future<Future<T>>。
  3. await表達(dá)式可以使用多次,但只能在async標(biāo)記的函數(shù)中使用。

Future

Future表示在將來(lái)某時(shí)獲取一個(gè)值的方式。當(dāng)一個(gè)返回Future的函數(shù)被調(diào)用的時(shí)候,做了兩件事情:

  1. 函數(shù)把自己放入隊(duì)列和返回一個(gè)未完成的Future對(duì)象
  2. 之后當(dāng)值可用時(shí),F(xiàn)uture帶著值變成完成狀態(tài)。
  //1.使用then按指定順序
  getName2().then((_) => getName1()).then((_) => getName3());
  //2.使用whenComplete()按指定順序
  getName2().whenComplete(() {
    getName1().whenComplete(() {
      getName3();
    });
  });
  //3.使用wait,一并返回完成值
  Future.wait([getName2(), getName1(), getName3()])
      .then((List re) {
    re.forEach((i) => print(i));
  });

Future與異常

//返回都是Future可以用鏈?zhǔn)浇Y(jié)構(gòu)
void goThrow() {
  new Future(() => print('start'))
      .then((_) => new Future.error("xxxxxx"))
      .whenComplete(() => print("run"))
      .then((_) => print("don't"))
      .catchError((e) => print(e));
}
//混合同步類型和異步類型的異常
main() {
 fun3().catchError((e){
   print('e = $e');
 });
}

fun1() {
  throw 'fun1 is error';
}

fun2() {
  throw 'fun2 is error';
}

//方法1:
//Future fun3(){
//  return new Future.sync((){
//    fun1();
//    return new Future(() {
//      fun2();
//    });
//  });
//}
//方法2:
Future fun3() async{
  fun1();
  return new Future(() {
    fun2();
  });
}

事件隊(duì)列

  1. Dart應(yīng)用程序僅有一個(gè)消息循環(huán),以及兩個(gè)隊(duì)列:event事件隊(duì)列和microtask微任務(wù)隊(duì)列。
  2. 事件隊(duì)列包含所有的外部事件,如:I/O、鼠標(biāo)事件、定時(shí)器、Isolate之間的消息等等。

當(dāng)main函數(shù)退出后,消息循環(huán)開始工作,首先FIFO的方式執(zhí)行所有微任務(wù)。接著,從事件隊(duì)列中提取消息并一條條處理,然后再執(zhí)行所有的微任務(wù),以此循環(huán),直到所有隊(duì)列為空。

如何調(diào)度任務(wù)

dart:async包中提供的如下API:

  1. Future類,可添加一個(gè)事件到事件隊(duì)列的末尾;
  2. 頂層函數(shù) scheduleMicrotask(),可添加一個(gè)任務(wù)到微任務(wù)隊(duì)列的末尾。

Future幾點(diǎn)注意事項(xiàng):

  1. 當(dāng)Future完成計(jì)算后,then()注冊(cè)的回調(diào)函數(shù)會(huì)立即執(zhí)行。需注意的是,then()注冊(cè)的函數(shù)并不會(huì)添加到事件隊(duì)列中,回調(diào)函數(shù)只是在事件循環(huán)中任務(wù)完成后被調(diào)用。
  2. 如果Future在then()被調(diào)用之前已經(jīng)完成計(jì)算,那么任務(wù)會(huì)被添加到微任務(wù)隊(duì)列中,并且該任務(wù)會(huì)執(zhí)行then()中注冊(cè)的回調(diào)函數(shù)。
  3. Future()和Future.delayed()構(gòu)造函數(shù)并不會(huì)立即完成計(jì)算。
  4. Future.value()構(gòu)造函數(shù)在微任務(wù)中完成計(jì)算,其他類似第2條。
  5. Future.sync()構(gòu)造函數(shù)會(huì)立即執(zhí)行函數(shù),并在微任務(wù)中完成計(jì)算,其他類似第2條。
//demo1
import 'dart:async';

main() async {
  Future f1 = new Future(() => null);
  Future f2 = new Future(() => null);
  Future f3 = new Future(() => null);

  f3.then((_) => print("1"));

  f2.then((_) {
    print("2");
    new Future(() => print("3"));
    f1.then((_) {
      print("4");
    });
  });
}
//運(yùn)行結(jié)果
2
4
1
3
//demo2
import 'dart:async';

main() {
  print('1');
  scheduleMicrotask(() => print('s1'));

  new Future.delayed(new Duration(seconds: 1), () => print('d1'));
  
  new Future(() => print('f1')).then((_) {
    print('f2');
    scheduleMicrotask(() => print('s3'));
  }).then((_) => print('f3'));

  new Future(() => print('f4'));

  scheduleMicrotask(() => print('s2'));

  print('2');
}
//打印結(jié)果:
1
2
s1
s2
f1
f2
f3
s3
f4
d1

注意點(diǎn):

  1. 盡量使用事件隊(duì)列,微任務(wù)要盡量簡(jiǎn)單,否則會(huì)引起鼠標(biāo)無(wú)反應(yīng)等。
  2. 為使應(yīng)用程序保持響應(yīng),避免在事件循環(huán)中添加計(jì)算密集型代碼
  3. 執(zhí)行計(jì)算密集型代碼的時(shí)候,另創(chuàng)建Isolate

生成器

生成器有兩種類型:同步生成器和異步生成器。

同步生成器:sync*

Iterable getNum(n) sync* {
  print("Begin");
  int k = 0;
  while (k < n) {
    //moveNext會(huì)返回true給調(diào)用者。
    //函數(shù)會(huì)在下次調(diào)用moveNext的時(shí)候恢復(fù)執(zhí)行。
    yield k++;
  }
  print("End");
}

goIterable() {
  //調(diào)用getNun立即返回Iterable
  var it = getNum(3).iterator;
  //調(diào)用moveNext方法時(shí)getNum才開始執(zhí)行
  while(it.moveNext()) {
    print(it.current);
  }
  print('over');
}
//打印結(jié)果
Begin
0
1
2
End
over

異步生成器:async*

Stream getNum(n) async* {
  print("Begin");
  int k = 0;
  while (k < n) {
    //不用暫停,數(shù)據(jù)流通過(guò)StreamSubscription進(jìn)行控制
    yield k++;
  }
  print("End");
}

goStream() {
  //調(diào)用getNum立即返回Stream,只有執(zhí)行了listen,函數(shù)才會(huì)開始執(zhí)行
  getNum(3).listen((v) {
    print(v);
  });
  print('over');
}
//打印結(jié)果
over
Begin
0
1
2
End
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容