跳到主要内容

Websocket 和同步执行

到目前为止,我们只介绍了如何创建一个 Future
这是有意为之的,因为 Future 是 Riverpod 应用程序构建方式的核心。 但是,如有必要,Riverpod 还支持其他格式。

特别是,除了 Future 类型的提供者程序,还存在灵活的类型:

  • 同步返回一个对象,例如创建“存储库”。
  • 返回一个 Stream,例如监听 websockets。

返回 Future 和返回 Stream 或 object 总体上非常相似。 本页将解释这些用例的细微差别和各种提示。

同步返回对象

若要同步创建对象,请确保提供者程序不返回 Future:


int synchronousExample(SynchronousExampleRef ref) {
return 0;
}

当提供者程序同步创建对象时,这会影响对象的使用方式。 具体而言,同步值不会被包装在“AsyncValue”中:

  Consumer(
builder: (context, ref, child) {
// value 没有使用 "AsyncValue "包装
int value = ref.watch(synchronousExampleProvider);

return Text('$value');
},
);

这种差异的后果是,如果提供者程序抛出异常,尝试读取该值会重新抛出错误。 或者,当使用 ref.listen 时,将调用 “onError” 回调。

可监听对象的注意事项

可监听对象,例如 ChangeNotifierStateNotifier 是不受支持的。
如果出于兼容性原因,您需要与其中一个对象进行交互, 一种解决方法是将其通知机制通过管道传递给 Riverpod。

/// 一个提供者程序,它创建 ValueNotifier 并在值更改时更新其监听器。

ValueNotifier<int> myListenable(MyListenableRef ref) {
final notifier = ValueNotifier(0);

// 当提供者程序被处置时处置通知者程序
ref.onDispose(notifier.dispose);

// 每当 ValueNotifier 更新时通知此提供者程序的监听器。
notifier.addListener(ref.notifyListeners);

return notifier;
}
信息

如果你多次需要这样的逻辑,值得注意的是, 逻辑是共享的!"ref" 对象被设计为可组合的。 这样就可以从提供者程序中提取处置/监听逻辑:

extension on Ref {
// 我们可以将之前的逻辑移至 Ref 扩展。
// 这使得能够重用提供者程序之间的逻辑
T disposeAndListenChangeNotifier<T extends ChangeNotifier>(T notifier) {
onDispose(notifier.dispose);
notifier.addListener(notifyListeners);
// 我们返回通知者程序以稍微简化使用
return notifier;
}
}


ValueNotifier<int> myListenable(MyListenableRef ref) {
return ref.disposeAndListenChangeNotifier(ValueNotifier(0));
}


ValueNotifier<int> anotherListenable(AnotherListenableRef ref) {
return ref.disposeAndListenChangeNotifier(ValueNotifier(42));
}

监听一个流

现代应用程序的一个常见用例是与 Websocket 交互, 例如与 Firebase 或 GraphQL 订阅进行交互。
与这些 API 的交互通常是通过监听一个 Stream

为了帮助实现这一点,Riverpod 自然地支持 Stream 对象。 与 Future 一样,该对象将转换为 AsyncValue


Stream<int> streamExample(StreamExampleRef ref) async* {
// 每 1 秒产生一个 0 到 41 之间的数字。
// 这可以替换为来自 Firestore 或 GraphQL 或其他任何东西的 Stream。
for (var i = 0; i < 42; i++) {
yield i;
await Future<void>.delayed(const Duration(seconds: 1));
}
}

class Consumer extends ConsumerWidget {

Widget build(BuildContext context, WidgetRef ref) {
// 该流被监听并转换为 AsyncValue。
AsyncValue<int> value = ref.watch(streamExampleProvider);

// 我们可以使用 AsyncValue 来处理加载/错误状态并显示数据。
return switch (value) {
AsyncValue(:final error?) => Text('Error: $error'),
AsyncValue(:final valueOrNull?) => Text('$valueOrNull'),
_ => const CircularProgressIndicator(),
};
}
}
信息

Riverpod 不知道自定义 Stream 实现,例如 RX 的 BehaviorSubject。 因此,返回 BehaviorSubject 不会同步向小部件公开,value 即使在创建时已经可用。

禁用从 Stream / Future 转换到 AsyncValue

默认情况下,Riverpod 会将 StreamFuture 转换为 AsyncValue。 尽管很少需要,但可以通过将返回类型包装在 Raw 泛型中来禁用此行为。

警告

通常不建议禁用转换 AsyncValue。 只有当您知道自己在做什么时才这样做。


Raw<Stream<int>> rawStream(RawStreamRef ref) {
// “Raw”是一个 typedef。
// 无需包装返回值的“原始”构造函数中的值。
return const Stream<int>.empty();
}

class Consumer extends ConsumerWidget {

Widget build(BuildContext context, WidgetRef ref) {
// 该值不再转换为 AsyncValue,
// 并且按原样返回创建的流。
Stream<int> stream = ref.watch(rawStreamProvider);
return StreamBuilder<int>(
stream: stream,
builder: (context, snapshot) {
return Text('${snapshot.data}');
},
);
}
}