棘手的蒸汽可观察合并在rxjs中

时间:2016-05-15 15:40:29

标签: javascript rxjs

我可以使用一些帮助。我正在尝试一个实验,我在按钮的鼠标上启动我的ajax请求,但是如果用户点击(或者如果用户点击时请求尚未完成,则消耗结果为尽快)。如果用户在未单击的情况下离开按钮,则应取消该请求。

我遇到的问题是如何将点击流与请求流合并。我不能使用withLatestFrom,因为如果请求在点击后完成,它将不会被使用。我也不能使用combineLatest,因为如果过去发生了任何点击,那么请求将被消耗,即使我目前只是将鼠标移除。 会喜欢一些指导。这是一个有趣的问题,但我被困了

const fetchContent = (url) => {
  const timeDelay$ = Rx.Observable.timer(1000); // simulating a slow request
  const request$ =  Rx.Observable.create(observer =>
    fetch(url, { mode: 'no-cors' })
      .then(json => {
         observer.onNext('res')
         observer.onCompleted()
      })
      .catch(e => observer.onError())
  )
  return timeDelay$.concat(request$)
}
const hover$ = Rx.Observable.fromEvent(myButton, 'mouseenter')
const leave$ = Rx.Observable.fromEvent(myButton, 'mouseleave')
const click$ = Rx.Observable.fromEvent(myButton, 'click')

const hoverRequest$ = hover$
  .flatMap(e =>
    fetchContent(e.target.getAttribute('href'))
      .takeUntil(leave$.takeUntil(click$))
  )

const displayData$ = click$
  .combineLatest(hoverRequest$)

displayData$.subscribe(x => console.log(x))

2 个答案:

答案 0 :(得分:2)

你实际上并不是很远。你真的错过了zip的包含。由于您真正需要传播的是两者鼠标单击要完成的请求。通过压缩请求和鼠标单击事件,您可以确保两者都不会发出。

const hover$ = Rx.Observable.fromEvent(myButton, 'mouseenter');
const leave$ = Rx.Observable.fromEvent(myButton, 'mouseleave');
const click$ = Rx.Observable.fromEvent(myButton, 'click');


//Make sure only the latest hover is emitting requests
hover$.flatMapLatest(() => {

  //The content request
  const pending$ = fetchContent();

  //Only cancel on leave if no click has been made
  const canceler$ = leave$.takeUntil(click$);

  //Combine the request result and click event so they wait for each other
  return Rx.Observable.zip(pending$, click$, (res, _) => res)

               //Only need the first emission
               .take(1)

               //Cancel early if the user leaves the button
               .takeUntil(canceler$);

});

答案 1 :(得分:1)

也许你可以把它概念化为三个事件(悬停,离开,点击你调用它们)触发三个动作(发出请求,取消请求,传递请求结果)修改状态(请求?传递请求?)。

现在这是在星期天晚上匆匆忙忙完成的,但运气稍微好一点,这样的事情可行:

function label(str) {return function(x){var obj = {}; obj[str] = x; return obj;}}
function getLabel(obj) {return Object.keys(obj)[0];}
const hover$ = Rx.Observable.fromEvent(myButton, 'mouseenter').map(label('hover'));
const leave$ = Rx.Observable.fromEvent(myButton, 'mouseleave').map(label('leave'));
const click$ = Rx.Observable.fromEvent(myButton, 'click').map(label('click'));

var initialState = {request : undefined, response : undefined, passResponse : false};

var displayData$ = Rx.Observable.merge(hover$, leave$, click$)
  .scan(function (state, intent){
          switch (getLabel(intent)) {
            case 'hover' : 
              if (!state.request) {
                state.request = someRequest;
                state.response$ = Rx.Observable.fromPromise(executeRequest(someRequest));
              }
            break;
            case 'leave' : 
              if (state.request && !state.passResponse) cancelRequest(someRequest);
              state.passResponse = false;
            break;
            case 'click' :
              if (!state.request) {
                state.response$ = Rx.Observable.fromPromise(executeRequest(someRequest));
              }
              state.passResponse = true;
          }
        }, initial_state)
  .filter(function (state){return state.passResponse;})
  .pluck('response$')
  .concatAll()