公告

微信

欢迎大家私信交流

Skip to content

Flutter异步编程笔记

来自专栏:掘金专栏

单线程下的异步模型

概念

同步和异步

同步:同一时间线,单一任务线,按时序执行

异步:同一时间线,多条任务线,按时序执行

任务的状态

未完成 完成但错误 完成且正确

单线程中的异步任务

任务分配

不阻塞任务完成的非冲突事件,可开新的任务线

异步的特点

尽可能的在最短时间完成任务

异步的回调函数

在某任务完成后,触发任务完成后的事件。该事件可以是我们预知任务完成后,该做的事件。

Dart语言中的异步

异步任务

异步任务,不会阻塞与自己无关的任务。

异步模型的延伸

单线程异步模型的局限性

单一线程没法,多开线程,完成多个需要自己完成的任务

多线程与异步的关系

多个线程,分发自己的任务给其他,完成自己的任务

Dart中如何解决单线程异步模型的局限性

移动/桌面客户端大多是网络、数据库访问等io密集型的任务

io密集型就是设备的资源交换密集

cpu密集型就是设备的计算资源占比大

Future类的使用

分析Future类

认识Future对象

  • sky_engine
    • async
    • future.dart

Future可以指定一个泛型,该类型就是期待的结果。

Future对象是在任务创建之前就产生的,对未来的结果我们是未知的,可能是成功也可能是失败。

我们通过then和catchError方法监听Future对象最终的结果,结果可以是异常,也可以是成功。

Future对任务回调的监听

  • then 成功后的监听方法
  • catchError 异常后的监听方法
  • whenComplete 无论成功/异常的监听方法

异步任务的异常抓取

期待返回值,判断返回值。

认识异步异常抓取

catchError中onError的细节

dart
Future<T> catchError(Function onError, {bool test(Object error)?});

Future. delayed(
  const Duration(seconds: 1),
  () => throw 401,
).then((value) {
  throw 'Unreachable';
}).catchError((err) {
  print('Error: $err'); // Prints 401.
}, test: (error) {
  return error is int && error >= 400;
});

FutureOr对象

该类型代表Future<T>T类型。也就是说,它是一类两型的特殊存在。

catchError第二参: test

判断是否error返回的条件

异步成功的回调方法

then方法的返回值

dart
/// 返回值为泛型R
Future<R> then<R>(FutureOr<R> onValue(T value), {Function? onError});

then方法中的onError

如果使用了onError那么catchError就会失效

whenComplete 方法

dart
Future<T> whenComplete(FutureOr<void> action());

任务结束时候需要执行的动作

async/await关键字的使用

使用await关键字修饰Future对象后,返回值是结果类型

async/await 使用的注意点

后面的代码是否需要await,会影响一个程序的丝滑性

Future总结总结

Future类里的方法的第二参数,都可以对返回的参数进行一次自定义校验。

await/async语法糖按需使用。

认识Stream类的使用

分析Stream对象

Stream存在的意义

对某一个任务,想分阶段的了解任务的完成程度。

读取文件认识Stream的使用

两个重要角色:发布者和订阅者

初步认识StreamSubscription

订阅流

dart
/// 调用listen方法
StreamSubscription<T> listen(void onData(T event)?,
    {Function? onError, void onDone()?, bool? cancelOnError});

/// 其他方法

// 暂停订阅
void pause([Future<void>? resumeSignal]);
// 恢复订阅
void resume();
// 终止订阅
Future<void> cancel();

综合理解Stream的使用

场景分析

红绿灯案例,写在flutter_study里面了

灯状态

dart
const int _kAllowMaxCount = 10;
const int _kWaitMaxCount = 3;
const int _kDenialMaxCount = 10;

enum SignalType {
  denial, // 拒绝 - 红灯
  allow, // 允许 - 绿灯
  wait, // 等待 - 黄灯
}

class SignalState {
  final int counter;
  final SignalType type;

  SignalState({
    required this.counter,
    required this.type,
  });

  SignalState next() {
    if (counter > 1) {
      return SignalState(type: type, counter: counter - 1);
    } else {
      switch (type) {
        case SignalType.allow:
          return SignalState(
              type: SignalType.denial, counter: _kDenialMaxCount);
        case SignalType.denial:
          return SignalState(type: SignalType.wait, counter: _kWaitMaxCount);
        case SignalType.wait:
          return SignalState(type: SignalType.allow, counter: _kAllowMaxCount);
      }
    }
  }
}

灯组件

dart
import 'package:flutter/material.dart';
import '../model/signalState.dart';

class SignalLamp extends StatelessWidget {
  final SignalState state;

  const SignalLamp({Key? key, required this.state}) : super(key: key);

  Color get activeColor {
    switch (state.type) {
      case SignalType.allow:
        return Colors.green;
      case SignalType.denial:
        return Colors.red;
      case SignalType.wait:
        return Colors.amber;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Container(
          padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
          decoration: BoxDecoration(
            color: Colors.black,
            borderRadius: BorderRadius.circular(30),
          ),
          child: Wrap(
            alignment: WrapAlignment.center,
            crossAxisAlignment: WrapCrossAlignment.center,
            spacing: 15,
            children: [
              lamp(color: state.type == SignalType.denial ? activeColor : null),
              lamp(color: state.type == SignalType.wait ? activeColor : null),
              lamp(color: state.type == SignalType.allow ? activeColor : null),
            ],
          ),
        ),
        Text(
          state.counter.toString(),
          style: TextStyle(
            fontWeight: FontWeight.bold,
            fontSize: 50,
            color: activeColor,
          ),
        )
      ],
    );
  }

  /// 单个信号灯
  Widget lamp({Color? color}) {
    return Container(
      width: 40,
      height: 40,
      decoration: BoxDecoration(
        color: color ?? Colors.grey.withOpacity(0.8),
        shape: BoxShape.circle,
      ),
    );
  }
}

信号灯页面

dart
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_study/StreamUse/widgets/signalLamp.dart';

import 'model/signalState.dart';

void main() {
  runApp(const App());
}

class App extends StatefulWidget {
  const App({super.key});

  @override
  State<App> createState() => _AppState();
}

class _AppState extends State<App> {
  final StreamController<SignalState> streamController = StreamController();

  SignalState _signalState = SignalState(counter: 10, type: SignalType.denial);

  bool hasError = false;

  @override
  void initState() {
    super.initState();
    streamController.stream.listen(
      emit,
      onError: (err) async {
        print(err);
        renderError();
        await Future.delayed(const Duration(seconds: 3));
        fixError();
        emit(_signalState.next());
      },
      // 如果 cancelOnError = true ,在监听到异常之后,就会取消监听 stream ,也就是说之后控制器添加的元素就不会触发监听了
      cancelOnError: false,
    );
    streamController.add(_signalState);
  }

  @override
  void dispose() {
    super.dispose();
    streamController.close();
  }

  void renderError() {
    hasError = true;
    setState(() {});
  }

  void fixError() {
    hasError = false;
  }

  void emit(SignalState state) async {
    _signalState = state;
    setState(() {});
    await Future.delayed(const Duration(seconds: 1));
    SignalState nextState = state.next();
    if (nextState.counter == 7 && nextState.type == SignalType.denial) {
      streamController.addError(Exception('Error Signal State'));
    } else {
      streamController.add(nextState);
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(builder: (context, child) {
      return Scaffold(
        body: Center(
            child: !hasError
                ? SignalLamp(state: _signalState)
                : const Text('Error Signal State')),
      );
    });
  }
}

异步生成器函数与Stream

Stream与Iterable

  • Iterable在时间和空间上都对元素保持持有关系
  • Stream只是在时间上监听若干元素的到来,并不在任意时刻都持有元素,更不会在空间上保持持有关系

通过异步生成器函数获取Stream对象

dart
class SignalStream{
  SignalState _signalState = SignalState(counter: 10, type: SignalType.denial);
  
  /// async* 修饰的方法需要返回一个Stream对象
  Stream<SignalState> createStream({int count = 100}) async*{
    for(int i = 0 ; i < count; i++){
      await Future.delayed(const Duration(seconds: 1));
      _signalState = _signalState.next();
      /// yield 关键字产出泛型结果对象
      yield _signalState;
    }
  }
}

上次更新于: