RxJS:仅在上一个Observable完成后才让Observable发出值

问题描述 投票:1回答:1

我目前正在尝试实现Angular结构指令,该指令将读取并存储HTML元素(及其所有子元素)的文本内容,在AfterViewInit上删除其内容,并延迟添加内容,一次输入一个字符,使它看起来像是实时键入的。

我通过分析目标节点的NodeType来做到这一点。如果是文本,它将把数据内容备份到一个数组中并继续下一个元素。如果是其他节点,则还将递归分析该节点。这样,我还可以修改目标元素的另一个HTMLElement中包含的文本。

要将文本写回节点,我正在使用Observables。每个节点的原始值一次发出一个字符,每个字符之间有60ms的延迟,然后将其重新添加到原始节点的内容中以模拟键盘输入延迟。在我的anaylze方法中,我还收集了前一个节点内容的长度,将Observable延迟了该数量* 60ms。我的意图是让下一个Observable仅在上一个Observable完成后才发出其值。

但是(可能由于Observable的异步特性)通常一个Observable会在前一个Observable完全完成之前开始发出值。这有时使页面看起来好像一次添加了多行,这是我要避免的行为。

这是我的Observable的样子:

from(this.mementos)
  .pipe(
    concatMap(
      memento => of(memento).pipe(delay(memento.previousNodeLength * 60)) // delay Observable by the time it takes to complete the previous node
    )
  )
  .subscribe(memento =>
    from(Array.from(memento.data))
      .pipe(concatMap(text => of(text).pipe(delay(60)))) // delay each character by 60ms
      .subscribe(c => {
        memento.node.data += c;
      })
  );

完成展示问题的Stackblitz工作:Stackblitz Example

如何修改代码,以便一次仅添加一个节点的内容?

angular rxjs reactive-programming angular-directive
1个回答
2
投票

使用n * 60计算延迟会给您确切的数字,但是如果使用n setTimeout()模拟相同的延迟,则它将不匹配,因为setTimeout()无法保证确切的超时。

因此,您可以删除嵌套的Observable,并使其成为一条链,使之使用concatMap首先将获取每个纪念品,然后获取每个字符。

from(this.mementos)
  .pipe(
    concatMap(memento => from(memento.data).pipe( // each memento
      concatMap(char => of({ node: memento.node, char }).pipe(delay(60))), // each char
    ))
  )
  .subscribe(({ node, char }) => {
    node.data += char;
  });

仅在前一个备忘录完成后,才会开始处理下一个备忘录。每个字符在嵌套的Observable内部延迟。

实时演示:https://stackblitz.com/edit/angular-ivy-njfysl?file=src/app/typewriter.directive.ts

© www.soinside.com 2019 - 2024. All rights reserved.