Flutter 最詳細的異步總結

1. Dart中事件循環模型

兩個任務隊列:

  • 微任務隊列 microtask queue
  • 事件隊列 event queue

「微任務」優先執行,沒有才執行「事件隊列」,「事件隊列」每次執行一次,「微任務」較少,IO、計時器、點擊、繪製屬於事件markdown

2. 任務調度

2.1 MicroTask隊列

void main() {
  print("main start");
  scheduleMicrotask(() => print('microtask'));
  print("main end");
}

flutter: main start
flutter: main end
flutter: microtask
複製代碼

2.2 Event隊列

void main() {
  print("main start");
  Future(() => print('microtask'));
  print("main end");
}

flutter: main start
flutter: main end
flutter: microtask
複製代碼

3 Future與FutureBuilder

3.1 Future使用

Future() Future.microtask() Future.sync() Future.value() Future.delayed() Future.error() Future.sync()網絡

void main() {
  print("main start");
  
  // 延時執行
  Future.delayed(Duration(seconds: 1), () => print('1秒後在Event queue中運行的Future'));
  
  // 同步執行,回當即執行
  Future.sync(() => print('同步運行的Future')).whenComplete(() => print('complete'));
  
  // 異步執行,then在異步方法執行完後按順序執行,
  // 出現錯誤會執行catchError方法
  // 無論有沒有錯誤最終都會執行whenComplete
  Future(() => print('task'))
      .then((_) => print('callback1'))
      .then((_) => print('callback2'))
      .catchError((error) => print("$error"))
      .whenComplete(() => print('complete'));

  print("main end");
}

// 結果輸出
flutter: main start
flutter: 同步運行的Future
flutter: main end
flutter: 在Microtask queue裏運行future
flutter: complete
flutter: task
flutter: callback1
flutter: callback2
flutter: complete
flutter: 1秒後在Event queue中運行的Future
複製代碼

3.2 Future.wait

等待多個任務完成後回調app

void main() {
  print("main start");

  Future.wait([
    Future.delayed(Duration(seconds: 2), () => "Hello"),
    Future.delayed(Duration(seconds: 4), () => "Flutter")
  ]).then((results) {
    print("${results[0]} + ${results[1]}");
  }).catchError((e) => print(e));

  print("main end");
}
  
// 輸出
flutter: main start
flutter: main end
flutter: Hello + Flutter
  
複製代碼

3.3 Completer

和Future最大的區別能夠手動控制完成時機less

/// Completer
  var completer = Completer();
  // completer中包含一個future
  var future = completer.future;
  // 設置future執行完成後的回調
  future.then((value) => print("then $value"));
  
  print('do something else');
  
  // 能夠控制完成時間,而後執行then回調
  completer.complete("done");

  print("main end");
}

// 輸出
flutter: main start
flutter: do something else
flutter: main end
flutter: then done
複製代碼

3.4 FutureBuilder

使用場景:依賴異步數據動態更新UI. FutureBuilder根據依賴的future動態構建自身異步

FutureBuilder({
  this.future,   // 依賴的future,一般爲一個異步耗時任務
  this.initialData,  // 初始數據,用戶設置的默認數據
  @required this.builder,  // Widget構建器,會被Future執行的不一樣階段屢次調用
})

// builder的構建方法
Function (BuildContext context, AsyncSnapshot snapshot)
// snapshot包含當前異步任務的狀態信息和結果信息,
// 好比能夠經過snapshot.connectionState獲取異步任務狀態
// snapshot.hasError判斷異步任務是否出現錯誤
複製代碼

FutureBuilder使用async

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  Future<String> mockNetworkData() async {
    return Future.delayed(Duration(seconds: 2), () => throw);
  }

  @override
  void initState() {
    // is is! 判斷對象是否爲指定類型, 如num,String
    assert(sayHello is Function, 'sayHello is not founction');

    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: FutureBuilder<String>(
          future: mockNetworkData(),
          builder: (BuildContext context, AsyncSnapshot snapshot) {
            if(snapshot.connectionState == ConnectionState.done) {
              if(snapshot.hasError) {
                // 請求失敗
                return Text("Error: ${snapshot.error}");
              } else {
                // 請求完成
                return Text("Contents: ${snapshot.data}");
              }
            } else {
              // 顯示loading
              return CircularProgressIndicator();
            }
          }
        )
      ),
    );
  }

複製代碼

4 async/await

asnc聲明的方法有以下意義ide

  • 方法返回一個Future
  • await只能在async中出現
  • 該方法會同步執行方法中的代碼,直到遇到第一個await,會等待await完成再執行下面的代碼,但該方法會在第一個await處返回,
  • 一旦由await引用的Future任務完成,await的下一行代碼當即執行
  • async函數中能夠有多個await,每碰見一個就返回一個Future,效果相似then串起來的回調
  • async函數能夠沒有await,執行完畢返回一個Future
void main() {
  fool();
}

fool() async {
  print('foo E');
  String v = await bar();
  print('foo X $v');
}

bar() async {
  print('bar E');
  return 'hello';
}

// 輸出
flutter: foo E
flutter: bar E
flutter: foo X hello
複製代碼

c947f5bd560668bdb64ee34bf650c750.png
如圖所示,await將代碼割裂成了兩部分,綠框中的代碼會同步直到遇到await會立刻返回一個Future,紅框中的代碼你能夠看做是在then裏面的回調方法,和下面的方法等效

fool() async {
  print('foo E');
  return Future.sync(bar).then((v) => print('foo X $v'));
}
複製代碼

5 Stream與StreamBuilder

Stream也用於接收異步數據,與Future不一樣在於能夠接收多個異步返回結果。能夠經過屢次觸發成功或失敗來傳遞數據或錯誤異常 使用場景:屢次讀取數據的異步任務場景,網絡內容下載,文件讀取等函數

5.1 Steam使用

void main() {
  print('main start');

  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),
      onDone: () => print("Done"));

  print('main end');
}

// 輸出
flutter: main start
flutter: main end
flutter: hello 1
flutter: Assertion failed
flutter: hello 3
flutter: Done
複製代碼

5.2 StreamBuilder使用

StreamBuilder用於配合Stream來展現流上事件(數據)變化的UI組件 StreamBuilder構造函數ui

StreamBuilder({
  Key key,
  this.initialData,
  Stream<T> stream,
  @required this.builder,
})
複製代碼

StreamBuilder使用this

class _MyHomePageState extends State<MyHomePage> {

  Stream<int> counter() {
    return Stream.periodic(Duration(seconds: 100), (i) => i);
  }

  @override
  void initState() {

    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: buildStream(context)
      ),
    );
  }

  Widget buildStream(BuildContext context) {
    return StreamBuilder<int>(
      stream: counter(),
      builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
        if (snapshot.hasError)
          return Text('Error: ${snapshot.error}');
        switch (snapshot.connectionState) {
          case ConnectionState.none:
            return Text('沒有Stream');
          case ConnectionState.waiting:
            return Text('等待數據...');
          case ConnectionState.active:
            return Text('active: ${snapshot.data}');
          case ConnectionState.done:
            return Text('Stream已關閉');
        }
        return null; // unreachable
      },
    );
  }
}

複製代碼

相關文章
相關標籤/搜索