gpt4 book ai didi

javascript - 在 RxJS 中制作打字计时器;跟踪打字时间

转载 作者:塔克拉玛干 更新时间:2023-11-02 23:00:13 30 4
gpt4 key购买 nike

这个问题是我之前问题的延伸,你可以在这里找到:

How to use RxJS to display a "user is typing" indicator?

在成功跟踪用户是否正在打字之后,我需要能够将该特定状态用作时钟的触发器。

逻辑很简单,本质上我希望在用户键入时运行一个时钟。但是当用户停止输入时,我需要时钟暂停。当用户再次开始输入时,时钟应继续累积。

我已经能够让它工作,但它看起来一团糟,我需要帮助重构它,这样它就不是一团意大利面条了。代码如下所示:

/*** Render Functions ***/

const showTyping = () =>
$('.typing').text('User is typing...');

const showIdle = () =>
$('.typing').text('');

const updateTimer = (x) =>
$('.timer').text(x);

/*** Program Logic ***/

const typing$ = Rx.Observable
.fromEvent($('#input'), 'input')
.switchMapTo(Rx.Observable
.never()
.startWith('TYPING')
.merge(Rx.Observable.timer(1000).mapTo('IDLE')))
.do(e => e === 'TYPING' ? showTyping() : showIdle());

const timer$ = Rx.Observable
.interval(1000)
.withLatestFrom(typing$)
.map(x => x[1] === 'TYPING' ? 1 : 0)
.scan((a, b) => a + b)
.do(console.log)
.subscribe(updateTimer)

这里是实时 JSBin 的链接:http://jsbin.com/lazeyov/edit?js,console,output

也许我会向您介绍代码的逻辑:

  1. 我首先构建一个流来捕获每个打字事件。
  2. 对于这些事件中的每一个,我将使用 switchMap 来:(a) 触发原始的“TYPING”事件,这样我们就不会丢失它,以及 (b) 触发“IDLE” "事件,1 秒后。您可以看到我将它们创建为单独的流,然后将它们合并在一起。这样,我得到一个流,指示输入框的“键入状态”。
  3. 我创建了第二个流,每秒发送一个事件。使用 withLatestFrom,我将这个流与之前的“键入状态”流结合起来。现在它们组合在一起,我可以检查输入状态是“IDLE”还是“TYPING”。如果他们正在打字,我会为事件赋予 1 值,否则为 0
  4. 现在我有一个 10 的流,我所要做的就是用 .scan() 将它们加回来> 并将其呈现给 DOM。

编写此功能的 RxJS 方法是什么?

编辑:方法 1 — 构建变化事件流

基于@osln 的回答。

/*** Helper Functions ***/

const showTyping = () => $('.typing').text('User is typing...');
const showIdle = () => $('.typing').text('');
const updateTimer = (x) => $('.timer').text(x);
const handleTypingStateChange = state =>
state === 1 ? showTyping() : showIdle();

/*** Program Logic ***/

const inputEvents$ = Rx.Observable.fromEvent($('#input'), 'input').share();

// streams to indicate when user is typing or has become idle
const typing$ = inputEvents$.mapTo(1);
const isIdle$ = inputEvents$.debounceTime(1000).mapTo(0);

// stream to emit "typing state" change-events
const typingState$ = Rx.Observable.merge(typing$, isIdle$)
.distinctUntilChanged()
.share();

// every second, sample from typingState$
// if user is typing, add 1, otherwise 0
const timer$ = Rx.Observable
.interval(1000)
.withLatestFrom(typingState$, (tick, typingState) => typingState)
.scan((a, b) => a + b, 0)

// subscribe to streams
timer$.subscribe(updateTimer);
typingState$.subscribe(handleTypingStateChange);

JSBin Live Demo

编辑:方法 2 — 在用户开始输入时使用 exhaustMap 启动计数器

基于 Dorus 的回答。

/*** Helper Functions ***/

const showTyping = () => $('.typing').text('User is typing...');
const showIdle = () => $('.typing').text('');
const updateTimer = (x) => $('.timer').text(x);

/*** Program Logic ***/

// declare shared streams
const inputEvents$ = Rx.Observable.fromEvent($('#input'), 'input').share();
const idle$ = inputEvents$.debounceTime(1000).share();

// intermediate stream for counting until idle
const countUntilIdle$ = Rx.Observable
.interval(1000)
.startWith('start counter') // first tick required so we start watching for idle events right away
.takeUntil(idle$);

// build clock stream
const clock$ = inputEvents$
.exhaustMap(() => countUntilIdle$)
.scan((acc) => acc + 1, 0)

/*** Subscribe to Streams ***/
idle$.subscribe(showIdle);
inputEvents$.subscribe(showTyping);
clock$.subscribe(updateTimer);

JSBin Live Demo

最佳答案

如果您想不断更新 UI,我认为没有任何方法可以使用计时器 - 我可能通过更改事件启动计时器来编写流有点不同 - 但您当前的流似乎也不错因为它已经是:

const inputEvents$ = Rx.Observable
.fromEvent($('#input'), 'input');

const typing$ = Rx.Observable.merge(
inputEvents$.mapTo('TYPING'),
inputEvents$.debounceTime(1000).mapTo('IDLE')
)
.distinctUntilChanged()
.do(e => e === 'TYPING' ? showTyping() : showIdle())
.publishReplay(1)
.refCount();

const isTyping$ = typing$
.map(e => e === "TYPING");

const timer$ = isTyping$
.switchMap(isTyping => isTyping ? Rx.Observable.interval(100) : Rx.Observable.never())
.scan(totalMs => (totalMs + 100), 0)
.subscribe(updateTimer);

直播here .


如果您不需要更新 UI 并且只想捕获输入的持续时间,您可以使用开始和停止事件并将它们映射到时间戳,例如:

const isTyping$ = typing$
.map(e => e === "TYPING");

const exactTimer$ = isTyping$
.map(() => +new Date())
.bufferCount(2)
.map((times) => times[1] - times[0])
.do(updateTimer)
.do(typedTime => console.log("User typed " + typedTime + "ms"))
.subscribe();

直播here .

关于javascript - 在 RxJS 中制作打字计时器;跟踪打字时间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41522222/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com