Flutter開發Dart極速入門 (Dart異步詳解)

異步

async和await

  • await 用於等待異步函數的結果

  • 要使用await,代碼必須在一個async函數中

  • 儘管async函數可能執行耗時的操作,但它不會等待這些操作。取而代之的是,該async函數僅執行到第一個await表達式。然後,它返回Future對象,只有await表達式完成後才恢復執行。

    main() {
      getName1();
      getName2();
    }
    
    getStr1() => print('getStr1');
    
    getStr2() => print('getStr2');
    
    Future<void> getName1() async {
    //  getStr1();
      await getStr1();  // 返回future對象,await表達式執行完成後繼續執行
      await getStr2();  // await表達式可以使用多次
      print('getName1');
    }
    
    getName2() => print('getName2');
    

then/ catchError/ whenComplete

如果需要監聽“完畢”這個狀態,那麼用whenComplete

需要監聽“成功”這個狀態,用then

需要監聽“失敗”這個狀態,用catchError

void runFuture() {
  Future(() => getFutureTask()) // 異步任務函數
      .then((i) => '++i ${++i}') // 異步任務執行完成後的子任務
      .then((s) => print('retult s: $s')) // s爲上個任務返回的結構
      .then((_) => print('_ is void'))  // _代表無意義的值(上個語句返回的是void), 習慣性寫法
      .then((_) => new Future.error('error')) // 返回一個error
      .then((_) => print('error 之後的執行語句.')) // error之後的語句不會執行, 直接跳轉到catchError
//      .catchError((e) => print(e))
      .catchError((e) => print(e), test: (o) {  // test: 自己實現處理異常的方法
        print('onTest: $o');
//        return true;  // 處理完成, 如果不實現test(), 默認也是返回true
        return false;   // 沒有處理完成, 繼續拋出異常
      })
      .catchError((e) => print('catch err2: $e'))
      .whenComplete(() => 'complete.');
}

Event-Looper

  • 所謂的消息循環的就是不斷從消息隊列中取出消息並處理他們直到消息隊列爲空。如下圖所示

    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-u8TVPJe5-1587916791157)(file:…\md_pic\image-20200311173821011.png)]

  • 消息隊列中的消息可能來自用戶輸入,文件I/O消息,定時器等。如上圖的消息隊列就包含了定時器消息和用戶輸入消息。

  • Dart的機制的main隔離, 如上圖所示, Dart中的Main Isolate只有一個Event Looper

  • Event Lopper中存在兩個Event Queue: Event Queue(事件隊列)以及Microtask Queue(微任務隊列)。

Event Queue 和 Microtask Queue

  • 優先全部執行完Microtask Queue中的Event
  • 直到Microtask Queue爲空時,纔會執行Event Queue中的Event
  • 當Event Looper正在處理Microtask Queue中的Event時候,Event Queue中的Event就停止了處理了,此時App不能繪製任何圖形,不能處理任何鼠標點擊,不能處理文件IO等等
  • 繪製圖形,處理鼠標點擊,處理文件IO等都是在Event Queue裏完成的

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-FqQtuUx9-1587916791162)(file:…\md_pic\queue.png)]

任務調度

  • 使用Future類,可以將任務加入到Event Queue的隊尾
  • 使用scheduleMicrotask函數,將任務加入到Microtask Queue隊尾

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-wEJ4TqKV-1587916791166)(file:/…\md_pic\image-20200311174546203.png)]

new Future() 詳解

  • 使用new Future() 將創建一個異步任務到Event隊列

  • Future中的then並沒有創建新的Event丟到Event Queue中,而只是一個普通的Function Call,在FutureTask執行完後,立即開始執行

  • 如果在then()調用之前Future就已經執行完畢了,那麼任務會被加入到microtask隊列中,並且該任務會執行then()中註冊的回調函數

  • Future.sync構造函數執行了它傳入的函數之後,也會立即創建Task丟到microtask Queue中執行

  • 當任務需要延遲執行時,可以使用new Future.delay()來將任務延遲執行

好了, 直接看實例

猜一猜下面代碼的打印順序

testFuture() {
  Future f = new Future(() => print('f1'));
//  Future f1 = new Future(() => null);	// 場景1
  Future f1 = new Future.delayed(Duration(seconds: 1));	// 場景2, f1被延遲1s入隊
  Future f2 = new Future(() => null);
  Future f3 = new Future(() => null);

  f3.then((_) => print('f2'));
  f2.then((_) {
    print('f3');
    new Future(() => print('f4'));
    f1.then((_) => print('f5'));
  });
  f1.then((m) => print('f6'));
  print('f7');
}

場景1 的輸出結果

//7 1 6 3 5 2 4

//'f7' - 非異步 最先執行
// ---- 下面的順序代表 Future 隊列的順序 ---------- fn代表隊列名稱, 'fn'(加引號的)代表輸出值
//  f   --> 'f1': 該Future在Event Queue中排第一個, 最先執行
//  f1  --> 'f6'
//  f2  --> 'f3': 執行完成後: 隊列中加入新的new Future, 並將'f5'放入f1的微隊列(scheduleMicrotask最先執行), 'f5'立刻被執行
//  f3  --> 'f2'
//  new Future  --> 'f4'

場景2, 延遲1s入隊的輸出結果

// 7 1 3 2 4 6 5

//'f7' - 非異步 最先執行
// ---- 下面的順序代表 Future 隊列的順序 ---------- fn代表隊列名稱, 'fn'(加引號的)代表輸出值
//  f   --> 'f1': 該Future在Event Queue中排第一個, 最先執行
//  f2  --> 'f3': 執行完成後: 隊列中加入新的new Future, 並將'f5'放入f1隊列
//  f3  --> 'f2'
//  new Future  --> 'f4'
//  ... 一秒後 ...
//  f1  --> 'f6' 1s後入隊, 之後執行'f5'

scheduleMicrotask() 詳解

  • 如果可以,儘量將任務放入event隊列中
  • 使用Future的then方法或whenComplete方法來指定任務順序
  • 爲了保持你app的可響應性,儘量不要將大計算量的任務放入這兩個隊列
  • 大計算量的任務放入額外的isolate中

上實例, 猜順序,

testScheduleMicrotask() {
  scheduleMicrotask(() => print('s1'));

  new Future.delayed(new Duration(seconds: 1), () => print('s2'));
  var f1 = new Future(() => print('s3'));
  f1.then((_) {
      print('s4');
      scheduleMicrotask(() => print('s5'));
    })
    .then((_) => print('s6'));
  var f2 = new Future(() => print('s10'));
  f2.then((_) => new Future(() => print('s11')))
    .then((_) => print('s12'));
  new Future(() => print('s7'));
  scheduleMicrotask(() => print('s8'));
  print('s9');
}

輸出結果如下 9 1 8 3 4 6 5 10 7 11 12 2

如果實在猜不到, 就不用猜了

直接聽我嗶嗶吧

// 先打印 's9'
// ----- micro 隊列 ------- task 隊列 --------
// 's1' // 先執行main中的微隊列, 最先輸出's1'
// 's8'	// 然後輸出's8', delay的's2'當然還是放在最後
// 接下來要按順序執行Event隊列了
// 此時Event隊列的順序是 f1 f2 new('f7')

//                      f1 -> 's3'	// 開始執行f1隊列中的內容
//                      f1 -> 's4'
//                      f1 -> 's6'  // f1執行完畢, 開始檢查他的微隊列
// f1_micro -> 's5'	// f1的微隊列被執行完畢

//                      f2 -> 's10'	// 開始執行f2隊列

// f2隊列中的new Future創建出來了new_f2, f2中未完成的任務都將被放入new f2
// 此時Event隊列的順序是 f1 f2 new('f7') new_f2
// 繼續執行, 檢查f2的微隊列, 微隊列內容爲空, 繼續執行new('f7')

//                      's7'

//                      new f2 -> 's11'
//                      new f2 -> 's12'

//                      's2'

隔離 - Isolate

Dart中使用isolate來代替thread, Isolate不是thread, 是一種獨立運行且不共享內存的worker, 所有Dart代碼都在自己的隔離區運行;

每個隔離區都有自己的堆內存, 以確保isolate的狀態不能被其他任何isolate訪問;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章