在RxJS中,是否可以在auditTime()操作中测试挂起值?

时间:2017-09-25 18:05:36

标签: javascript rxjs rxjs5

我正在使用RxJS的.auditTime(500)操作(docs)作为尾随节流:我想每500毫秒最多发出一次服务器调用。

当服务器调用完成时,我需要询问是否有更多待处理的服务器调用,或者暂时是否清除了缓冲区,以便我可以以状态消息的形式将此信息传递给用户,例如“正在保存...”和“保存。”

这大致是看起来的样子。

saveToServerObservable
  .do(() => {
    // gets called every time
    setStatus(Status.SAVING);
  })
  .auditTime(500) // wait 500 ms and emit no more than once per 500 ms
  .flatMap(data => axios({
    method: "post",
    url: "/saveurl",
    data: data,
  }))
  .map(response => response.data)
  .do(data => {
    // here I want to know whether there are pending values from the
    // auditTime() operation above or if the buffer is currently clear
    const pendingSaves = ???;
    if (!pendingSaves) {
     setStatus(Status.SAVED);
    }
  })
  .subscribe();

正如您在最后的.do()操作中所看到的,我想知道.auditTime(500)操作中是否存在待定值。我怎样才能实现这样的目标呢?

干杯!

2 个答案:

答案 0 :(得分:1)

我认为您可以使用scan并稍微修改您的链来实现您想要的目标:

const inc = new Subject();
const dec = new Subject();

const counter = Observable.merge(dec.mapTo(-1), inc.throttleTime(500).mapTo(1))
    .scan((acc, val) => acc + val, 0)
    .map(val => val > 0);

saveToServerObservable
  .do(() => {
    // gets called every time
    setStatus(Status.SAVING);
    inc.next();
  })
  .auditTime(500) // wait 500 ms and emit no more than once per 500 ms
  .flatMap(data => axios({
    method: "post",
    url: "/saveurl",
    data: data,
  }))
  .do(() => dec.next())
  .map(response => response.data)
  .withLatestFrom(counter, (data, pendingSaves) => {
    if (!pendingSaves) {
     setStatus(Status.SAVED);
    }
  })
  .subscribe();

整个想法位于合并counterinc的{​​{1}} Observable中。这两个Observable使用dec递增和递减计数器。

scan()也与inc相关联,与.throttleTime(500)完全相反,因为当您致电.auditTime(500)时,您始终知道这会使setStatus(Status.SAVING);发出一个项目,因此你可以立即递增计数器。

然后.auditTime(500)只是将计数器与远程调用的结果合并,这是您可以检查来自withLatestFrom的最新发射的位置。

答案 1 :(得分:0)

增加和减少计数器太容易受到错误的影响,所以我最终采用了完全不同的方法。我现在分别跟踪本地数据是否“脏”。我使用这个脏信号向用户显示“Saving ...”vs“Saved”消息:

  1. 每次用户进行本地修改时,我都会在本地更新数据并将dirty设置为true
  2. 每次保存操作后,服务器都会使用最新版本的数据进行响应。
  3. 收到回复后,我会根据服务器返回的内容来区分数据的本地版本,如果匹配,我将dirty设置为false
  4. 在每次修改时将dirty设置为true

    我在每次用户进行编辑时定义Rx.Subject。每次收到信号时,我都会将dirty设置为true

    // stream of signals to save the active document
    const userEditSignal$ = new Rx.Subject();
    
    const savePrototype = () => {
      userEditSignal$.next();
    };
    
    userEditSignal$.subscribe(() => {
      // runs for each call to save the active document
      store.commit("SET_DIRTY", true);
    });
    

    观察dirty状态以决定何时保存到服务器

    每当dirty值发生变化时,我们就会知道这一点,这与每次设置时都不一样。

    const observeState = (store, getter) => {
      // irrelevant details redacted
    }
    
    // emits only when `dirty` changes, not every time it's set
    const shouldSaveToServer$ = observeState(store, state => state.dirty);
    

    创建请求对象和服务器响应流

    此自定义时序逻辑取代了对auditTime()运算符的需求。

    const saveToServerSignal$ = shouldSaveToServer$.switchMap(shouldSave => {
      return shouldSave ?
        // as long as we should save, save every 500 ms
        Rx.Observable.interval(500) :
        // when we should not, stop
        Rx.Observable.never();
    });
    
    // create a request object for each save-to-server signal
    const saveRequest$ = saveToServerSignal$
      .mapTo(store.state.activeDocument)
      .map(createSaveRequest);
    
    // 
    const saveResponse$ = saveRequest$
      // sends immediately
      .flatMap(request => axios(request));
    

    在每个响应中,检查diff本地文档和从服务器返回的版本

    如果他们同意,我们可以将dirty设置为false

    saveResponse$
      .map(response => response.data)
      .do(savedDocument => {
        const activeDocument = store.state.activeDocument;
    
        // update just `created`, `modified`, and `user`
        store.commit({
          type: "UPDATE_ACTIVE_DOCUMENT",
          // irrelevant details omitted
        });
    
        // diff current state and saved document (function details omitted)
        const activeAndSavedDocsMatch = diff(activeDocument, savedDocument);
        if (activeAndSavedDocsMatch) {
          store.commit("SET_DIRTY", false);
        }
      })
      .subscribe();