Future常用的异步函数

227 阅读4分钟

Future常用的异步函数

Future是一种表示可能异步返回值的对象。当我们需要执行一个可能会阻塞主线程的操作时,可以使用Future来使这个操作异步执行,这样就不会阻塞UI线程。

一、  异步函数

Future: 

Future是一种表示可能会返回结果的未来值的对象。可以使用async和await关键字来处理Future。

Future fetchData() async {
  // 模拟耗时操作
  await Future.delayed(Duration(seconds: 2));
  return 'Data fetched successfully!';
}

void main() async {
  print('Fetching data...');
  String data = await fetchData();
  print(data);
}

async和await: 

async关键字用于修饰函数,表示该函数是一个异步函数,可以在其中使用await关键字来等待异步操作的结果。

Future fetchData() async {
  // 模拟耗时操作
  await Future.delayed(Duration(seconds: 2));
  return 'Data fetched successfully!';
}

void main() async {
  print('Fetching data...');
  String data = await fetchData();
  print(data);
}

Future.delayed: 

可以使用Future.delayed创建一个延迟一段时间后返回结果的Future。

void main() async {
  print('Start');
  await Future.delayed(Duration(seconds: 2), () {
    print('Delayed action');
  });
  print('End');
}

Future.wait: 

如果需要同时等待多个异步操作完成,可以使用Future.wait。

Future fetchUser() {
  return Future.delayed(Duration(seconds: 2), () => 'User fetched');
}

Future fetchPosts() {
  return Future.delayed(Duration(seconds: 3), () => 'Posts fetched');
}

void main() async {
  print('Fetching data...');
  List> futures = [fetchUser(), fetchPosts()];
  List results = await Future.wait(futures);
  print(results);
}

Future.value:

是Flutter中的一个静态方法,用于创建一个已经完成(已解析)的Future对象,该对象的结果值为指定的值。

Future.value()方法的作用是立即返回一个已经完成的Future对象,且该Future对象的结果值就是指定的value值。这意味着可以通过该Future对象进行异步操作的编程模型,如使用await关键字等。

void main() async {
  Future future = Future.value(42);
  int result = await future;
  print(result); // 输出: 42
}

在上述示例中,Future.value(42)会创建一个已经完成的Future对象,结果值为42。通过await关键字,可以等待该Future对象的结果,然后将结果赋值给result变量,并打印出结果值42。

Future.value()方法在某些情况下非常有用,例如将同步的结果值转换为Future对象,或者作为其他异步操作的初始值。它提供了一种简便的方式来创建并返回一个已经完成的Future对象。

Future.doWhile:

Future.doWhile 是 Future 类的一个实例方法,用于执行一个异步操作的循环,直到指定的条件不再满足为止。

Future doWhile(FutureOr Function() action)

参数 action 是一个回调函数,它将被反复调用执行,直到返回的结果为 false。回调函数 action 可以是一个 Future 类型的函数,也可以是一个同步函数。

使用 Future.doWhile 方法可以实现异步的循环操作,以处理某些需要重复执行的异步任务,直到条件不再满足为止。

void main() async {
  int count = 0;
  
  await Future.doWhile(() async {
    await Future.delayed(Duration(seconds: 1));
    count++;
    print(count);
    
    return count < 5;
  });
  
  print('Loop completed');
}

Future.forEach:

Future.forEach 是 Future 类的一个实例方法,用于对一个集合进行异步迭代操作,并对集合中的每个元素执行指定的异步操作。

Future forEach(void Function(T element) action)

参数 action 是一个回调函数,它将被异步调用以处理集合中的每个元素。回调函数 action 接受一个参数 element,表示集合中的当前元素。

使用 Future.forEach 方法可以方便地对集合进行异步迭代操作,处理集合中的每个元素的异步任务,等待所有的任务完成后返回一个 Future 对象。

void main() async {
  List numbers = [1, 2, 3, 4, 5];
  
  await Future.forEach(numbers, (int number) async {
    await Future.delayed(Duration(seconds: 1));
    print(number);
  });
  
  print('Iteration completed');
}

Future.microtask:

Future.microtask 是 Future 类的一个静态方法,用于将一个微任务(microtask)添加到事件循环中,在当前事件循环的微任务队列中立即执行。

微任务是指一种高优先级的任务,它会在事件循环的任务队列中的其他任务之前执行。使用 Future.microtask 方法可以将一个回调函数添加到当前微任务队列中,以便在当前事件循环中的其他任务执行完成后立即执行。

static Future microtask(FutureOr computation())

参数 computation 是一个回调函数,它将被添加到当前微任务队列中执行。回调函数 computation 可以是一个 Future 类型的函数,也可以是一个同步函数。

使用 Future.microtask 方法可以在当前事件循环的微任务队列中执行一些高优先级的任务,而不会延迟其他任务的执行。

void main() {
  print('Start');
  
  Future.microtask(() {
    print('Inside microtask');
  });
  
  print('End');
}

在上述示例中,我们在 main 函数中使用 Future.microtask 方法添加了一个微任务。该微任务会在当前事件循环的任务队列中的其他任务执行完成后立即执行。因此,在打印 "Start" 和 "End" 之间,微任务中的回调函数会被执行,打印出 "Inside microtask"。

需要注意的是,Future.microtask 方法的回调函数在当前事件循环中的微任务队列中执行,因此不会创建新的事件循环,也不会引发事件循环的重新调度。这使得微任务的执行速度非常快,适用于需要立即执行的高优先级任务,例如状态更新、回调通知等。

另外,需要注意避免滥用 Future.microtask 方法,以免阻塞事件循环的正常执行。只有在必要的情况下,才应该使用微任务。

Future.sync:

Future.sync 是 Future 类的一个静态方法,用于创建一个同步执行的 Future 对象。

static Future sync(T computation())

参数 computation 是一个回调函数,它将在当前执行上下文中同步执行。回调函数 computation 可以是一个任意类型的函数,返回值类型为 T。

使用 Future.sync 方法可以将同步的计算操作封装为一个 Future 对象,以便与异步操作进行一致的编程模型。

void main() {
  Future future = Future.sync(() {
    return 42;
  });
  
  future.then((value) {
    print(value); // 输出: 42
  });
  
  print('End');
}

在上述示例中,我们使用 Future.sync 方法创建了一个同步的 Future 对象。回调函数中的计算操作直接返回了一个整数值 42。通过 future.then 方法,我们可以注册一个回调函数,当 Future 对象完成时将被调用。在回调函数中,我们打印出了 Future 对象的结果值 42。

需要注意的是,尽管 Future.sync 方法的回调函数是同步执行的,但返回的 Future 对象仍然具有异步的特性,因为它是 Future 类的实例。因此,可以在返回的 Future 对象上使用异步的方法和操作,例如 then、catchError 等。

另外,由于 Future.sync 方法的回调函数是在当前执行上下文中同步执行的,所以在回调函数中避免进行长时间的阻塞操作,以免影响整个应用程序的响应性。

二、  异步组件

在Flutter中,有一些用于处理异步操作的常用组件。这些组件可用于优化用户界面,显示加载状态,处理错误等。以下是一些常用的异步组件:

1、  FutureBuilder

用于根据 Future 的不同状态构建界面。它接收一个 Future 对象,并根据异步操作的状态(未完成、完成、出错)来构建相应的UI。

Future fetchData() async {
  await Future.delayed(Duration(seconds: 2));
  return 'Data fetched successfully';
}

Widget build(BuildContext context) {
  return FutureBuilder(
    future: fetchData(),
    builder: (BuildContext context, AsyncSnapshot snapshot) {
      if (snapshot.connectionState == ConnectionState.waiting) {
        return CircularProgressIndicator();
      } else if (snapshot.hasError) {
        return Text('Error: ${snapshot.error}');
      } else {
        return Text('Data: ${snapshot.data}');
      }
    },
  );
}

2、  StreamBuilder

用于监听 Stream 的事件并构建相应的UI。它接收一个 Stream 对象,并根据数据流的事件(数据、错误、完成)来构建界面。

Stream countStream() async* {
  for (int i = 1; i <= 5; i++) {
    await Future.delayed(Duration(seconds: 1));
    yield i;
  }
}

Widget build(BuildContext context) {
  return StreamBuilder(
    stream: countStream(),
    initialData: 0,
    builder: (BuildContext context, AsyncSnapshot snapshot) {
      if (snapshot.connectionState == ConnectionState.waiting) {
        return CircularProgressIndicator();
      } else if (snapshot.hasError) {
        return Text('Error: ${snapshot.error}');
      } else {
        return Text('Count: ${snapshot.data}');
      }
    },
  );
}

3、  RefreshIndicator

提供了下拉刷新的功能,用于更新显示的数据。它通常与一个 ListView 或 GridView 组件结合使用。

List<String> dataList = ['Item 1', 'Item 2', 'Item 3'];

Future<void> refreshData() async {
  await Future.delayed(Duration(seconds: 2));
  setState(() {
    dataList = ['Updated Item 1', 'Updated Item 2', 'Updated Item 3'];
  });
}

Widget build(BuildContext context) {
  return RefreshIndicator(
    onRefresh: refreshData,
    child: ListView.builder(
      itemCount: dataList.length,
      itemBuilder: (BuildContext context, int index) {
        return ListTile(
          title: Text(dataList[index]),
        );
      },
    ),
  );
}

4、  StreamBuilder 和 FutureBuilder 的衍生组件:

Flutter社区提供了许多基于 StreamBuilder 和 FutureBuilder 的衍生组件,用于处理更特定的异步场景,例如:

   - BlocBuilder:与状态管理库(如BLoC、Provider、Riverpod等)结合使用,用于构建基于数据流的界面。

   - ValueListenableBuilder:用于监听 ValueListenable 对象的变化并构建相应的界面。

   - `Animated

Builder:根据动画的值来构建界面,通常与 AnimationController` 和其他动画类一起使用。

三、  Flutter处理异步操作的结果和错误

在Flutter中,Future 类提供了许多方法来处理异步操作的结果和错误。以下是这些方法的说明:

1、  then

注册一个回调函数,该函数会在异步操作成功完成时被调用,并接收异步操作的结果作为参数。

Future<String> fetchData() async {
  await Future.delayed(Duration(seconds: 2));
  return 'Data fetched successfully';
}

fetchData().then((data) {
  print(data);
});

2、  catchError 或 onError:

注册一个回调函数,该函数会在异步操作发生错误时被调用,并接收错误信息作为参数。

Future<String> fetchData() async {
  await Future.delayed(Duration(seconds: 2));
  throw Exception('Error occurred');
}

fetchData().catchError((error) {
  print('Error: $error');
});

3、  error:

获取异步操作的错误信息,如果异步操作发生错误的话。

Future<String> fetchData() async {
  await Future.delayed(Duration(seconds: 2));
  throw Exception('Error occurred');
}

fetchData().then((data) {
  print(data);
}).catchError((error) {
  print('Error: ${error.toString()}');
});

4、  whenComplete:

注册一个回调函数,在异步操作完成时(不管成功或失败)被调用,不接收任何参数。

Future<String> fetchData() async {
  await Future.delayed(Duration(seconds: 2));
  return 'Data fetched successfully';
}

fetchData().then((data) {
  print(data);
}).catchError((error) {
  print('Error: $error');
}).whenComplete(() {
  print('Async operation completed');
});

5、  timeout:

设置异步操作的超时时间,并返回一个新的 Future,如果超时则会触发错误。

Future<String> fetchData() async {
  await Future.delayed(Duration(seconds: 5));
  return 'Data fetched successfully';
}

fetchData().timeout(Duration(seconds: 3), onTimeout: () {
  throw TimeoutException('Operation timed out');
}).then((data) {
  print(data);
}).catchError((error) {
  print('Error: $error');
});