我可以使用一些帮助。我正在尝试一个实验,我在按钮的鼠标上启动我的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))
答案 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()