Dart中的異步與事件循環

前言

Dart做爲Flutter的開發語言,瞭解Dart的機制是必可少的。本篇文章就介紹一下Dart的異步操做與事件循環機制。java

異步操做咱們都知道在開發過程當中,若是有耗時操做,咱們通常都會使用異步任務解決,以防主線程卡頓。markdown

事件循環是Dart中處理事件的一種機制。Flutter中就是經過事件循環來驅動程序的運行,這點與Android中的Handler有點相似。異步

Dart的事件循環機制

Dart語言是單線程模型的語言。這也就意味着Dart在同一時刻只能執行一個操做,其餘操做在這個操做以後執行。那麼Dart的其餘操做如何執行呢?在Dart中經過Event Loop來驅動事件的執行。async

  • Dart程序啓動時會建立兩個隊列,分別是MicroTak隊列(微服務隊列)和Event隊列(事件隊列);
  • 而後執行main()方法;
  • 最後啓動事件循環;

上面的圖片描述了Dart的事件循環機制。能夠看到,Dart執行事件的流程是:函數

  1. 執行main();
  2. 執行MicroTask隊列中事件;
  3. 執行事件隊列中的事件;

能夠看到,main()方法的優先級最高,執行結束後,才執行兩個隊列中事件。其中MicroTask隊列的優先級又高於Event隊列的優先級。微服務

MicroTask隊列和Event隊列

MicroTask 隊列適用於很是簡短且須要異步執行的內部動做或者想要在Event執行前作某些操做,由於在MicroTask執行完之後,還須要執行Event隊列中事件。oop

在使用Dart時異步事件應該優先考慮使用Event隊列。ui

Dart中的異步

Future

Future 是一個異步執行而且在將來的某一個時刻完成(或失敗)的任務。每個Future都會執行一個事件而且將事件加入到Event隊列中去。spa

factory Future(FutureOr<T> computation()) {
    _Future<T> result = new _Future<T>();
    Timer.run(() {
      try {
        result._complete(computation());
      } catch (e, s) {
        _completeWithErrorCallback(result, e, s);
      }
    });
    return result;
  }
複製代碼

從上面Future的構造函數代碼能夠看到,經過Timer的run()方法將運算過程發送到事件隊列中去,而後經過_complete()方法獲取了提交給Future的運算結果,最終返回一個Future對象。pwa

void main(){
    print('Before the Future');
    Future((){
        print('Running the Future');
    }).then((_){
        print('Future is complete');
    });
    print('After the Future');
    Future<String>.microtask(() {
        return "microtask";
  	}).then((String value) {
    	print(value);
  	});
}
複製代碼

以上代碼的運行結果是:

Before the Future
After the Future
microtask
Running the Future
Future is complete
複製代碼

能夠看到,這裏先執行的是main()方法中的打印信息,而後打印了microtask,最後才執行了Future。這裏也能夠證實Future是在事件隊列中執行的。

async、await關鍵字

async與await關鍵字配合使用能夠更加優雅的處理dart中的異步。對於有async修飾的函數來講,它會同步執行函數中的代碼,直到遇到第一個await關鍵字,而後將await修飾的代碼提交到事件隊列中處理,在await修飾的代碼執行完畢後會當即執行剩下的代碼,而剩下的代碼將在微任務隊列中執行。

void methodAsync() async {
  sleep(Duration(seconds: 1));
  Future<String>(() {
    return "future 1";
  }).then((String value) {
    print(value);
  });
  print("async start");
  await microTaskAndEvent(); //輸出一個微任務打印 microtask 以及一個事件隊列任務打印 futrue
  print("async end");
}

void main() {
    methodAsync();
  	print("Hello World");
}
複製代碼

以上代碼的輸出結果是:

async start
Hello World
microtask
async end
future 1
future
複製代碼

從打印結果能夠看出,

  • 這裏同步執行methodAsync()方法,即便在睡眠1s的狀況下,也首先輸出了async start;而後輸出Hello World;
  • 接下來執行了一個Future1,而後執行await關鍵字後面的函數;
  • microTaskAndEvent()方法包括一個微任務和事件。這裏能夠看到打印了microtask接着打印了async end,這裏能夠證實async在await關鍵字以後的代碼是放到微任務中執行的。
  • 最後打印了事件隊列中的任務。

Isolate

Isolate從名稱上理解是獨立的意思。在dart中,Isolate有着本身的內存而且不存在經過共享內存的方式實現Isolate之間的通訊,同時Isolate是單線程的計算過程。

Isolate的特色與使用:

  • Isolate有着本身的內存而且不存在經過共享內存的方式實現Isolate之間的通訊;
  • 每一個Isolate都有本身的事件循環;
  • 在Isolate中是經過Port進行通訊的。Port分爲兩種一種是receive port用於接收消息。另一種是send port用於發送消息;
  • 使用Isolate時,能夠經過spwan()方法啓動一個Isolate

接下來經過一個例子展現Isolate的通訊以及建立過程。

main() async {
  var receivePort = new ReceivePort();
  /**  * 初始化另一個Isolate,  * 將main isolate的send port發送到child isolate中,  * 用於向main isolate發送信息  */
  await Isolate.spawn(echo, receivePort.sendPort);

  // child isolate的第一個信息做爲child isolate的send port,因爲向child isolate發送信息
  var sendPort = await receivePort.first;

  var msg = await sendReceive(sendPort, "hello");
  print('received $msg');
  msg = await sendReceive(sendPort, "close");
  print('received $msg');
}

// child isolate的入口
echo(SendPort sendPort) async {
  // 打開一個接收端口
  var port = new ReceivePort();
  //監聽接收的信息
  port.listen((dynamic msg) {
    print("child receive $msg");
    var data = msg[0];
    SendPort replyTo = msg[1];
    replyTo.send(data);
    if (data == "close") port.close(); //關閉接收端口
  });

  // 向其餘的isolate通知child isolate的發送端口.
  sendPort.send(port.sendPort);
}
//發送信息
Future sendReceive(SendPort port, msg) {
  ReceivePort response = new ReceivePort();
  port.send([msg, response.sendPort]);
  return response.first;
}
複製代碼

總結

以上就是關於Dart的異步以及事件循環原理。理解這些內容是必要的,由於在不少的開發場景都會用到異步,同時須要很好的理解Dart的事件處理機制。

相關文章
相關標籤/搜索