【问题标题】:Confusing behavior of rxjs operator `delay`rxjs 运算符“延迟”的令人困惑的行为
【发布时间】:2019-07-08 16:26:27
【问题描述】:

我对 rxjs 运算符 delay 有点困惑。

当我使用from 创建的假 observable 对其进行测试时,我只看到 initial 延迟:

const { from } = Rx;
const { delay, tap } = RxOperators;

from([1, 2, 3, 4]).pipe(
  tap(console.log),
  delay(1000));

(您可以将此代码 sn-p 复制并粘贴到rxviz。)

我在其中放置了一个tap,以确保from 实际上将数组项作为单独的值而不是单个数组值发出。

最初的延迟不是我所期望的,但至少 the docs 是这样说的:

[...] 这个操作符将源 Observable 时间移动了以毫秒表示的时间量。值之间的相对时间间隔被保留。

但是,当我使用从事件创建的 observable 对其进行测试时,我会在每个发出的值之前看到延迟

const { fromEvent } = Rx;
const { delay } = RxOperators;

fromEvent(document, 'click')
  .pipe(delay(1000))

这里发生了什么?为什么delay 在这两种情况下的行为不同?

【问题讨论】:

  • 如果您想在发射项目之间延迟 1 秒,请尝试 throttleTime(1000)
  • throttleTime 将忽略在传入时间段内发生的任何事件。

标签: rxjs reactivex rxjs-pipeable-operators


【解决方案1】:

delay 所做的就是它所说的:每当它接收到一个值时,它会在延迟期间保持该值,然后发出它。它对接收到的每个值执行相同的操作。 delay不会改变流中项目之间的相对时间。

所以,当您执行from([1,2,3,4]).pipe(delay(1000)) 时,会发生什么:

  • 时间 0:from 发出 1
  • 时间 0:delay 看到 1 并启动 timer1
  • 时间 0:from 发出 2
  • 时间 0:delay 看到 2 并启动 timer2
  • ...
  • 时间 1000:timer1 完成,delay 发出 1
  • 时间 1000:timer2 完成,delay 发出 2
  • ...

因此,因为所有 4 个值都是快速连续发出的,所以您实际上只会看到一个初始延迟,然后所有 4 个值都被发出到下游。实际上,每个值都比最初发出时延迟了 1 秒。

如果您想将项目“分开”以使它们至少相隔 1 秒,那么您可以执行以下操作:

const source = from([1, 2, 3, 4])
const spread = source.pipe(concatMap(value => of(value).pipe(delay(1000))));
spread.subscribe(value => console.log(value));

这会将每个单独的值转换为一个可观察的,该可观察的会在延迟后发出该值,然后连接这些可观察的。这意味着每个项目的计时器在前一个项目的计时器完成之前不会开始计时。

【讨论】:

  • 哦,当然!现在我觉得自己很愚蠢。我可以在fromEvent 的示例中注意到它,因为双击在可视化中也显示为双击。感谢您详细介绍。
  • @Brandon 你将如何在不使用间隔的情况下逐步增加 RxJS 延迟? stackoverflow.com/q/64625561/3073280
【解决方案2】:

您点击流并获取发出的值,然后将它们传送到延迟,延迟一秒钟后发出它们。管道中的每个函数都会返回一个新的 observable,它会向管道中的下一个函数发出一个值。 Tap 返回同一个尚未延迟的 observable,而 delay 返回一个在一秒后发出的 observable。

const { from } = rxjs;
const { delay, tap } = rxjs.operators;

from([1, 2, 3, 4]).pipe(
  tap(val => { console.log(`Tap: ${val}`); }),
  delay(1000)).subscribe(val => { console.log(`Sub: ${val}`); });
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.2/rxjs.umd.min.js"></script>

如果您在延迟之后点击,那么您会在延迟之后看到它们。

const { from } = rxjs;
const { delay, tap } = rxjs.operators;

from([1, 2, 3, 4]).pipe(
  delay(1000),
  tap(val => { console.log(`Tap: ${val}`); })).subscribe(val => { console.log(`Sub: ${val}`); });
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.2/rxjs.umd.min.js"></script>

【讨论】:

  • 但问题是使用from 生成的值都是在初始延迟之后立即发出的。相反,我希望在他们每个人之前有一个延迟,这就是fromEvent 的做法。
  • 这就是 from 的工作原理,它创建了一个立即发出值的流。延迟从它接收到之前的 observable 的发射时仅延迟一秒钟。如果您希望值在彼此之后发出一秒钟,请使用间隔。
  • 什么是“立即发出值”的流?据我了解,Observable 只能通过调用next 发出,并且每个nexted 值都应用于pipe 中的运算符。你说得好像有一种特殊的next 阻止了pipe 运算符的应用,除了首先发出的值。
  • Observables 是流,当您从 from([1,2,3,4]) 创建一个时,它会调用 next(1)、next(2)、next(3) 和 next(4)所有这些都是连续的,它们之间没有延迟。添加延迟运算符会将发射延迟一秒,但它们都已同时发射。
  • 只需在上面的那两个上点击运行代码 sn-p,看看在延迟之前和延迟之后点击的区别。
【解决方案3】:

在第一个代码 sn-p 中,您正在逐个元素地发出数组元素。先延迟,再处理数组元素。

'from' 和 'pipe' 使 'delay' 执行一次。管道序列处理,首先延迟,然后点击,点击,点击,点击。

在第二个代码 sn-p 中,您正在发射对象(它们到达),因此每个对象都会发生一次延迟。

'fromEvent' 和 'pipe' 使每个事件都“延迟”。管道序列处理每个事件前的延迟。

【讨论】:

  • from 不是这样工作的。你说的是of。查看更新后的第一个 sn-p。
  • 对不起,我还是不明白。你说“'from' 和 'pipe' 让 'delay' 执行一次”,但为什么会这样呢?此外,如果您检查控制台,您会看到 console.log 被调用 before delay 被应用。据我了解,pipe 中的运算符会在每次在观察者/订阅者上调用 next 时应用。由于from calls next for every item 我预计每个项目都会出现延迟。
  • OP 的流中没有任何东西发出数组,数组中的每个值都是单独发出的。这就是 from 对数组所做的事情,of 会发出一个数组。
猜你喜欢
  • 2020-05-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-09
  • 1970-01-01
  • 2022-11-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多