likes
comments
collection
share

Dart中异步编程之Future详解同步VS异步 同步方法或操作是指一个任务在执行时,必须等待另一个任务完成后才能继续执

作者站长头像
站长
· 阅读数 17

同步VS异步

同步方法或操作是指一个任务在执行时,必须等待另一个任务完成后才能继续执行。换句话说:所有任务按照顺序一个接一个地执行,调用者在等待操作完成之前会被阻塞,无法继续执行其他任务。异步方法或操作是指一个任务在执行时,不需要等待前一个任务完成就可以继续执行其他任务。异步操作通常通过回调、Promise、Future等机制来处理结果。调用者不需要等待操作完成,可以继续执行其他任务,当操作完成时,会通过回调或其他机制通知调用者。换句话说:所有任务可以并行执行多个任务(不同任务在不同的CPU核上执行,或者单核分时段执行不同任务)。当执行耗时的任务的时候,我们就需要使用异步任务,防止耗时长导致掉帧甚至出现卡顿以及ANR,比如:文件读写,网络请求,数据库读写,以及CPU密集型的任务的处理。

Future简介

Future是Dart中一个非常重要的异步编程的概念,它是指未来某个时间需要完成的操作或者任务,它可以让我们在不阻塞当前任务的情况下执行一个任务,并在任务完成后获得相应的结果。

Future状态转换

Dart中异步编程之Future详解同步VS异步 同步方法或操作是指一个任务在执行时,必须等待另一个任务完成后才能继续执 代码示例如下所示:

void main() async{
  age().then((value) {
    print(value);
  }, onError: (e) {
      print("catchError  $e");
  }
  );
}

Future<int> age() async {
  throw Exception('发生错误');
  // return 35;
}

Future有三种状态:初始状态(Uncompleted),数据完成状态(Completed with data),异常完成状态(Completed with error)。Future创建的时候是初始状态(当调用age方法的时候),当调用then回调函数中有两个参数,一个onValue方法回调(代表有返回值),一个onError方法回调(代表返回异常)。Future执行结果要么是执行onValue,要么是执行onError。除了用onError回调,还可以使用catchError方法监听异常错误信息。上述代码,当Future完成时会打印错误信息。

多个Future的顺序执行

在Flutter中处理复杂的异步代码时,尤其是嵌套多个Future.then()调用时,代码很快就会变得难以阅读和维护。这通常被称为“回调地狱”或“then 地狱”。示例代码如下所示:

void fetchData() {
  fetchDataFromApi()
      .then((data) {
        processData(data).then((processedData) {
          saveData(processedData).then((result) {
            print('Data saved successfully!');
          }).catchError((error) {
            print('Failed to save data: $error');
          });
        }).catchError((error) {
          print('Failed to process data: $error');
        });
      })
      .catchError((error) {
        print('Failed to fetch data: $error');
      });
}

then方法的嵌套回调让代码的可读性和可维护性大大降低。Dart的async和await关键字帮助您编写看起来更像同步代码的异步代码,使其更易于阅读和维护。

Future<void> fetchData() async {
  try {
    final data = await fetchDataFromApi();
    final processedData = await processData(data);
    await saveData(processedData);
    print('Data saved successfully!');
  } catch (error) {
    print('An error occurred: $error');
  }
}

更改之后的例子有三方面的好处。

  1. 可读性:代码从上到下阅读,更容易理解。
  2. 错误处理:您可以使用 try-catch 块在一个地方处理错误,从而简化逻辑。
  3. 可维护性:在添加、删除或修改步骤中的异步请求非常简单。 这个编程范式让异步的嵌套代码复杂度直接从N变为了1,这样就让代码出错的概率大大减小。

多个Future都执行完毕

有时候业务需求需要多个异步任务都执行完毕,才能执行某些操作,这时候就需要用Future.wait()方法来实现。wait的适用场景:比如请求数据库中A表的数据,请求数据库中B表的数据,两个数据集再汇合成需要的数据,这时候wait方法。

void main() async{
  print(fetchData());
  print(await Future.wait([fetchData(), fetchData2()]));
  print("All done!");
}

Future<String> fetchData() async {
  await Future.delayed(const Duration(seconds: 5));
  return "fetchData";
}
Future<String> fetchData2() async {
  await Future.delayed(const Duration(seconds: 3));
  return "fetchData2";
}

fetchData和fetchData2这两个异步操作都返回结果之后,Future.wait才会把结果按照List集合返回,wait的耗时是两个异步操作的最长耗时也就是5秒。如果Future没有调用then方法,或者前面没有await,会返回Instance of '_Future'。Future.delayed(const Duration(seconds: 3))是为了让方法变成耗时的方法,这样的代码多用于测试代码,模拟延时。代码的输出如下:

Instance of '_Future<String>'
[fetchData, fetchData2]
All done!

多个Future同时执行,返回最快的结果

有时候需要多个异步任务中最快任务,执行完毕就返回结果,然后可以执行其他业务操作,这时候就需要用Future.any()方法来实现。any的适用场景:比如可以通过网络请求数据,也可以通过本机器的缓存来请求数据,两个同时请求用any返回最快的数据即可。

void main() async {
  print("begin");
  print(await Future.any([fetchData(), fetchData2()]));
  print("fastest done!");
}

Future<String> fetchData() async {
  await Future.delayed(const Duration(seconds: 5));
  return "fetchData";
}

Future<String> fetchData2() async {
  await Future.delayed(const Duration(seconds: 3));
  return "fetchData2";
}

Future在Flutter UI业务中的使用

在创建Widget时,initState()方法会被调用一次,这使其成为运行只应调用一次的Future的理想场所。可以根据FutureBuilder中状态不同,进行不同的UI展示。示例代码如下所示:

    class MyWidget extends StatefulWidget {
  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  late Future<String> _futureData;

  @override
  void initState() {
    super.initState();
    // Future is initialized here and will run only once
    _futureData = fetchData();
  }

  Future<String> fetchData() async {
    // Simulate a network call or other async task
    await Future.delayed(Duration(seconds: 2));
    return "Data Loaded";
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: FutureBuilder<String>(
        future: _futureData,
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            return CircularProgressIndicator();
          } else if (snapshot.hasError) {
            return Text("Error: ${snapshot.error}");
          } else {
            return Text("Data: ${snapshot.data}");
          }
        },
      ),
    );
  }
}

总结

异步编程是软件开发过程中为了提高性能不得不考虑的一个方面。使用Future,可以将耗时任务放在后台执行,从而防止主线程卡顿。Future是一个非常底层的类,它提供了很多方法,比如等待所有Future执行完成的wait()方法,返回执行最快的Future的any()方法,这些方法的组合就能完成基本的业务需求开发。祝您编码愉快!

转载自:https://juejin.cn/post/7403547816915795994
评论
请登录