Skip to content

Commit

Permalink
📃 docs(zh-Hans/websockets_sync):
Browse files Browse the repository at this point in the history
  • Loading branch information
yang-lile committed Dec 9, 2023
1 parent 2983ed1 commit 0d6f111
Show file tree
Hide file tree
Showing 10 changed files with 107 additions and 27 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: Websockets and synchronous execution
title: Websocket 和同步执行
---

import {
Expand All @@ -15,52 +15,95 @@ import pipeChangeNotifier from "!!raw-loader!./websockets_sync/pipe_change_notif
import sharedPipeChangeNotifier from "!!raw-loader!./websockets_sync/shared_pipe_change_notifier.dart";
import changeNotifierProvider from "!!raw-loader!./websockets_sync/change_notifier_provider.dart";

<!---
So far, we've only covered on how to create a `Future`.
This is on purpose, as `Future`s are the core of how Riverpod applications
should be built. _But_, Riverpod also supports other formats if necessary.
-->
到目前为止,我们只介绍了如何创建一个 `Future`。
这是有意为之的,因为 `Future` Riverpod 应用程序构建方式的核心。
_但是_,如有必要,Riverpod 还支持其他格式。

<!---
In particular, instead of a `Future`, providers are free to:
-->
特别是,除了 `Future` 类型的提供者,还存在灵活的类型:

<!---
- Synchronously return an object, such as to create a "Repository".
- Return a `Stream`, such as to listen to websockets.
-->
- 同步返回一个对象,例如创建“存储库”。
- 返回一个 `Stream`,例如监听 websockets。

<!---
Returning a `Future` and returning a `Stream` or an object
is quite similar overall. Think of this page as
an explanation of subtle differences and various tips for those use-cases.
-->
返回 `Future` 和返回 `Stream` object 总体上非常相似。
本页将解释这些用例的细微差别和各种提示。

<!---
## Synchronously returning an object
-->
## 同步返回对象

<!---
To synchronously create an object, make sure that your
provider does not return a Future:
-->
若要同步创建对象,请确保提供者程序不返回 Future:

<AutoSnippet {...syncDefinition} />

<!---
When a provider synchronously creates an object,
this impacts how the object is consumed.
In particular, synchronous values are not wrapped
in an "AsyncValue":
-->
当提供者程序同步创建对象时,这会影响对象的使用方式。
具体而言,同步值不会被包装在“AsyncValue”中:

<AutoSnippet raw={syncConsumer} />

<!---
The consequence of this difference is that if your provider
throws, trying to read the value will rethrow the error.
Alternatively, when using `ref.listen`, the "onError" callback
will be invoked.
-->
这种差异的后果是,如果提供者程序抛出异常,尝试读取该值会重新抛出错误。
或者,当使用 `ref.listen` 时,将调用 “onError” 回调。

<!---
### Listenable objects considerations
-->
### 可监听对象的注意事项

<When codegen={true}>

<!---
Listenable objects such as `ChangeNotifier` or `StateNotifier` are not supported.
If, for compatibility reasons, you need to interact with one of such objects,
one workaround is to pipe their notification mechanism to Riverpod.
-->
可监听对象,例如 `ChangeNotifier` `StateNotifier` 是不受支持的。
如果出于兼容性原因,您需要与其中一个对象进行交互,
一种解决方法是将其通知机制通过管道传递给 Riverpod。

<AutoSnippet raw={pipeChangeNotifier} />

:::info
<!---
In case you need such logic many times, it is worth noting that
the logic shared! The "ref" object is designed to be composable.
This enables extracting the dispose/listening logic out of the provider:
-->
如果你多次需要这样的逻辑,值得注意的是,
逻辑是共享的!"ref" 对象被设计为可组合的。
这样就可以从提供者程序中提取处置/监听逻辑:

<AutoSnippet raw={sharedPipeChangeNotifier} />
:::
Expand All @@ -69,47 +112,86 @@ This enables extracting the dispose/listening logic out of the provider:

<When codegen={false}>

<!---
When not using code-generation, Riverpod offers "legacy" providers
to support `ChangeNotifier` and `StateNotifier` out of the box:
`ChangeNotifierProvider` and `StateNotifierProvider`. Using them is
similar to using other kinds of providers. The main difference is
that they will both listen and dispose of the returned object automatically.

-->
当不使用代码生成时,Riverpod 提供了“传统”的提供者程序来支持
`ChangeNotifier` `StateNotifier` 开箱即用:
`ChangeNotifierProvider` `StateNotifierProvider`。
使用它们就像使用其他类型的提供者程序一样。
主要区别在于它们将自动侦听和释放返回的对象。

<!---
These providers are not recommended for new business logic.
But they can be helpful when interacting with legacy code,
such as when migrating from `pkg:provider` to Riverpod.
-->
不建议将这些提供者程序用于新的业务逻辑。
但是,在与旧代码混合编写时(例如从 `pkg:provider` 迁移到 Riverpod 时),
它们可能会有所帮助。

<AutoSnippet raw={changeNotifierProvider} />

</When>

<!---
## Listening to a Stream
-->
## 监听一个流

<!---
A common use-case of modern applications is to interact with websockets,
such as with Firebase or GraphQL subscriptions.
Interacting with those APIs is often done by listening to a `Stream`.
-->
现代应用程序的一个常见用例是与 Websocket 交互,
例如与 Firebase GraphQL 订阅进行交互。
与这些 API 的交互通常是通过监听一个 `Stream`。

<!---
To help with that, Riverpod naturally supports `Stream` objects.
Like with `Future`s, the object will be converted to an `AsyncValue`:
-->
为了帮助实现这一点,Riverpod 自然地支持 `Stream` 对象。
`Future` 一样,该对象将转换为 `AsyncValue`:

<AutoSnippet {...streamProvider} />

:::info
<!---
Riverpod is not aware of custom `Stream` implementations, such as
RX's `BehaviorSubject`.
As such, returning a `BehaviorSubject` will not expose the `value`
synchronously to widgets, even if already available on creation.
-->
Riverpod 不知道自定义 `Stream` 实现,例如 RX `BehaviorSubject`。
因此,返回 `BehaviorSubject` 不会同步向小部件公开,`value` 即使在创建时已经可用。
:::

<!---
## Disabling conversion of `Stream`s/`Future`s to `AsyncValue`
-->
## 禁用从 `Stream` / `Future` 转换到 `AsyncValue`

<!---
By default, Riverpod will convert `Stream`s and `Future`s to `AsyncValue`.
Although rarely needed, it is possible to disable this behavior by wrapping
the return type in a `Raw` typedef.
-->
默认情况下,Riverpod 会将 `Stream` `Future` 转换为 `AsyncValue`。
尽管很少需要,但可以通过将返回类型包装在 `Raw` 泛型中来禁用此行为。

:::caution
<!---
It is generally discouraged to disable the `AsyncValue` conversion.
Do so only if you know what you are doing.
-->
通常不建议禁用转换 `AsyncValue`。
只有当您知道自己在做什么时才这样做。
:::

<AutoSnippet raw={rawUsage} />
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';

/* SNIPPET START */
final myProvider = ChangeNotifierProvider<ValueNotifier<int>>((ref) {
// Will listen to and dispose of the ValueNotifier.
// Widgets can then "ref.watch" this provider to listen to updates.
// 将监听并处置 ValueNotifier
// 然后,小部件可以使用 "ref.watch" 对提供程序侦听更新。
return ValueNotifier(0);
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,15 @@ import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'pipe_change_notifier.g.dart';

/* SNIPPET START */
/// A provider which creates a ValueNotifier and update its listeners
/// whenever the value changes.
/// 一个提供程序,它创建 ValueNotifier 并在值更改时更新其侦听器。
@riverpod
ValueNotifier<int> myListenable(MyListenableRef ref) {
final notifier = ValueNotifier(0);

// Dispose of the notifier when the provider is destroyed
// 当提供者被销毁时处置通知者
ref.onDispose(notifier.dispose);

// Notify listeners of this provider whenever the ValueNotifier updates.
// 每当 ValueNotifier 更新时通知此提供程序的侦听器。
notifier.addListener(ref.notifyListeners);

return notifier;
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ part 'raw_usage.g.dart';
/* SNIPPET START */
@riverpod
Raw<Stream<int>> rawStream(RawStreamRef ref) {
// "Raw" is a typedef. No need to wrap the return
// value in a "Raw" constructor.
// Raw”是一个 typedef
// 无需包装返回值的“原始”构造函数中的值。
return const Stream<int>.empty();
}

class Consumer extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
// The value is no-longer converted to AsyncValue,
// and the created stream is returned as is.
// 该值不再转换为 AsyncValue
// 并且按原样返回创建的流。
Stream<int> stream = ref.watch(rawStreamProvider);
return StreamBuilder<int>(
stream: stream,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ part 'shared_pipe_change_notifier.g.dart';

/* SNIPPET START */
extension on Ref {
// We can move the previous logic to a Ref extension.
// This enables reusing the logic between providers
// 我们可以将之前的逻辑移至 Ref 扩展。
// 这使得能够重用提供者之间的逻辑
T disposeAndListenChangeNotifier<T extends ChangeNotifier>(T notifier) {
onDispose(notifier.dispose);
notifier.addListener(notifyListeners);
// We return the notifier to ease the usage a bit
// 我们返回通知程序以稍微简化使用
return notifier;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ part 'codegen.g.dart';
/* SNIPPET START */
@riverpod
Stream<int> streamExample(StreamExampleRef ref) async* {
// Every 1 second, yield a number from 0 to 41.
// This could be replaced with a Stream from Firestore or GraphQL or anything else.
// 1 秒产生一个 0 到 41 之间的数字。
// 这可以替换为来自 Firestore GraphQL 或其他任何东西的 Stream。
for (var i = 0; i < 42; i++) {
yield i;
await Future<void>.delayed(const Duration(seconds: 1));
Expand All @@ -20,10 +20,10 @@ Stream<int> streamExample(StreamExampleRef ref) async* {
class Consumer extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
// The stream is listened to and converted to an AsyncValue.
// 该流被监听并转换为 AsyncValue
AsyncValue<int> value = ref.watch(streamExampleProvider);

// We can use the AsyncValue to handle loading/error states and show the data.
// 我们可以使用 AsyncValue 来处理加载/错误状态并显示数据。
return switch (value) {
AsyncValue(:final error?) => Text('Error: $error'),
AsyncValue(:final valueOrNull?) => Text('$valueOrNull'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';

/* SNIPPET START */
final streamExampleProvider = StreamProvider.autoDispose<int>((ref) async* {
// Every 1 second, yield a number from 0 to 41.
// This could be replaced with a Stream from Firestore or GraphQL or anything else.
// 1 秒产生一个 0 到 41 之间的数字。
// 这可以替换为来自 Firestore GraphQL 或其他任何东西的 Stream。
for (var i = 0; i < 42; i++) {
yield i;
await Future<void>.delayed(const Duration(seconds: 1));
Expand All @@ -16,10 +16,10 @@ final streamExampleProvider = StreamProvider.autoDispose<int>((ref) async* {
class Consumer extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
// The stream is listened to and converted to an AsyncValue.
// 该流被监听并转换为 AsyncValue
AsyncValue<int> value = ref.watch(streamExampleProvider);

// We can use the AsyncValue to handle loading/error states and show the data.
// 我们可以使用 AsyncValue 来处理加载/错误状态并显示数据。
return switch (value) {
AsyncValue(:final error?) => Text('Error: $error'),
AsyncValue(:final valueOrNull?) => Text('$valueOrNull'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ void main() {
/* SNIPPET START */
Consumer(
builder: (context, ref, child) {
// The value is not wrapped in an "AsyncValue"
// value 没有使用 "AsyncValue "包装
int value = ref.watch(synchronousExampleProvider);

return Text('$value');
Expand Down
2 changes: 1 addition & 1 deletion website/src/documents_meta.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export const documentTitles = {
"from_provider/quickstart": "快速开始",
"from_provider/provider_vs_riverpod": "Provider 对比 Riverpod",
"from_provider/motivation/motivation": "动机",
"essentials/websockets_sync": "Websockets and synchronous execution",
"essentials/websockets_sync": "Websocket 和同步执行",
"essentials/testing": "Testing your providers",
"essentials/side_effects": "执行副作用",
"essentials/provider_observer": "Logging and error reporting",
Expand Down

0 comments on commit 0d6f111

Please sign in to comment.