如何确定在去抖时间内发出的值的数量?
How can I determine the number of values has been emitted during the debounce time?
给定:
处理“放大”显示请求的 NgRx 效果。每次用户单击适当的按钮时它都会收到通知。
public readonly zoomIn$ = createEffect(
() =>
this.actions$.pipe(
ofType(zoomIn),
tap(() => {
scale();
}),
),
{ dispatch: false },
);
注意:zoomIn
操作不能也不包含任何负载。仅将其视为触发器
问题:
重新绘制会消耗资源,在某些情况下会占用几秒钟才能获得新比例。所以如果你想连续放大几次,你将不得不等待。
解法:
通过使用 debounceTime
运算符推迟 scale()
函数的调用并等待用户多次点击。听起来不错。唯一的问题是 debounceTime
通知我们最新的值。我们需要的是一些被 debounceTime
运算符静音的值(用户的点击)。
从更一般的角度来看,任务听起来像:如何计算源流发出并被 debounceTime
运算符静音的值的计数?
我的解决方案 是创建一个实现目标的自定义 pipable 运算符。
我们需要什么?当然,我们需要一个debounceTime
运算符。
.pipe(
debounceTime(300),
)
然后我们应该计算已经发出的值的数量。有一个 scan
运算符,它看起来很像 well-known reduce
函数。我们将给它一个初始值,并将在每个从源流接收到的值上增加一个计数器。我们必须将它放在 debounceTime
运算符之前。它现在看起来像一个索引流。
.pipe(
scan(acc => acc + 1, 0),
debounceTime(300),
)
当debounceTime
通知我们最新索引时,我们如何知道muted values的个数?我们必须将它与之前发出的索引进行比较。可以使用 pairwise
运算符接收先前的值。然后使用 map
运算符来区分它们。
.pipe(
scan(acc => acc + 1, 0),
debounceTime(300),
pairwise(),
map(([previous, current]) => current - previous),
)
如果您在当前状态下尝试此操作,您会发现有些地方不对劲,这是第一次无法正常工作。问题在于 pairwise
运算符。它会发出成对的值(先前的和当前的),因此它会等到至少有两个值才开始发出成对的值。公平吗?是的?这就是为什么我们需要稍微作弊并使用 startWith
运算符提供第一个值(即 0)。
最终实现
/**
* Emits a notification from the source Observable only after a particular time span has passed without another source emission,
* with an exact number of values were emitted during that time.
*
* @param dueTime the timeout duration in milliseconds for the window of time required to wait for emission silence before emitting the most recent source value.
* @returns MonoTypeOperatorFunction
*/
export const debounceCounter =
(dueTime: number): MonoTypeOperatorFunction<number> =>
(source: Observable<unknown>): Observable<number> =>
new Observable(observer =>
source
.pipe(
scan(acc => acc + 1, 0),
debounceTime(dueTime),
startWith(0),
pairwise(),
map(([previous, current]) => current - previous),
)
.subscribe({
next: x => {
observer.next(x);
},
error: err => {
observer.error(err);
},
complete: () => {
observer.complete();
},
}),
);
用法示例
public readonly zoomIn$ = createEffect(
() =>
this.actions$.pipe(
ofType(zoomIn),
debounceCounter(300),
tap(times => {
// scale n-times
}),
),
{ dispatch: false },
);
给定:
处理“放大”显示请求的 NgRx 效果。每次用户单击适当的按钮时它都会收到通知。
public readonly zoomIn$ = createEffect(
() =>
this.actions$.pipe(
ofType(zoomIn),
tap(() => {
scale();
}),
),
{ dispatch: false },
);
注意:zoomIn
操作不能也不包含任何负载。仅将其视为触发器
问题:
重新绘制会消耗资源,在某些情况下会占用几秒钟才能获得新比例。所以如果你想连续放大几次,你将不得不等待。
解法:
通过使用 debounceTime
运算符推迟 scale()
函数的调用并等待用户多次点击。听起来不错。唯一的问题是 debounceTime
通知我们最新的值。我们需要的是一些被 debounceTime
运算符静音的值(用户的点击)。
从更一般的角度来看,任务听起来像:如何计算源流发出并被 debounceTime
运算符静音的值的计数?
我的解决方案 是创建一个实现目标的自定义 pipable 运算符。
我们需要什么?当然,我们需要一个debounceTime
运算符。
.pipe(
debounceTime(300),
)
然后我们应该计算已经发出的值的数量。有一个 scan
运算符,它看起来很像 well-known reduce
函数。我们将给它一个初始值,并将在每个从源流接收到的值上增加一个计数器。我们必须将它放在 debounceTime
运算符之前。它现在看起来像一个索引流。
.pipe(
scan(acc => acc + 1, 0),
debounceTime(300),
)
当debounceTime
通知我们最新索引时,我们如何知道muted values的个数?我们必须将它与之前发出的索引进行比较。可以使用 pairwise
运算符接收先前的值。然后使用 map
运算符来区分它们。
.pipe(
scan(acc => acc + 1, 0),
debounceTime(300),
pairwise(),
map(([previous, current]) => current - previous),
)
如果您在当前状态下尝试此操作,您会发现有些地方不对劲,这是第一次无法正常工作。问题在于 pairwise
运算符。它会发出成对的值(先前的和当前的),因此它会等到至少有两个值才开始发出成对的值。公平吗?是的?这就是为什么我们需要稍微作弊并使用 startWith
运算符提供第一个值(即 0)。
最终实现
/**
* Emits a notification from the source Observable only after a particular time span has passed without another source emission,
* with an exact number of values were emitted during that time.
*
* @param dueTime the timeout duration in milliseconds for the window of time required to wait for emission silence before emitting the most recent source value.
* @returns MonoTypeOperatorFunction
*/
export const debounceCounter =
(dueTime: number): MonoTypeOperatorFunction<number> =>
(source: Observable<unknown>): Observable<number> =>
new Observable(observer =>
source
.pipe(
scan(acc => acc + 1, 0),
debounceTime(dueTime),
startWith(0),
pairwise(),
map(([previous, current]) => current - previous),
)
.subscribe({
next: x => {
observer.next(x);
},
error: err => {
observer.error(err);
},
complete: () => {
observer.complete();
},
}),
);
用法示例
public readonly zoomIn$ = createEffect(
() =>
this.actions$.pipe(
ofType(zoomIn),
debounceCounter(300),
tap(times => {
// scale n-times
}),
),
{ dispatch: false },
);