在我的应用程序中,我有一个返回这样的observable的服务:
public genericService(params) {
//Do some stuff
//...
return this.http.post('http://foo.com', params)
.map((response) => {
//Do some generic stuff
//...
return someData;
})
.catch((error: any) => {
//Manage error in a generic way + do some generic stuff
//...
return Observable.throw(error);
});
}
let debouncePointer = debounceObservable(genericService, 200);
public genericServiceDebounce(params) {
return debouncePointer(params);
}
现在在另一个地方,我想把这个功能称为
genericServiceDebounce(params)
.subscribe((response) => {
//Do some non-generic stuff
}, (error) => {
//Manage error in a non-generic way + do some non-generic stuff
});
但我没有成功实施debounceObservable()函数。
我尝试了基于Promise等效项(https://github.com/moszeed/es6-promise-debounce/blob/master/src/es6-promise-debounce.js):
的实现debounceObservable(callback, delay, immediate?) {
let timeout;
return function () {
let context = this, args = arguments;
return Observable.create((observer) => {
let later = function () {
timeout = null;
if(!immediate) {
observer.next(callback.apply(context, args));
//observer.onCompleted(); // don't know if this is needed
}
};
let callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, delay);
if(callNow) {
observer.next(callback.apply(context, args));
//observer.onCompleted(); // don't know if this is needed
}
});
}
}
但这并不像预期的那样奏效。 使用Promises时,返回resolve(anotherPromise)允许您调用:
genericServiceDebounce().then(response => {
})
使用Observable时,返回observer.next(anotherObservable)会返回一个嵌入的observable,这意味着你应该调用:
genericServiceDebounce().subscribe(obs => {
obs.subscribe(response => {
})
})
你将如何实现debounceObservable()函数?(以类似的方式实现)
澄清1:我找到了Observable.debounce()函数,但这会使观察者去抖动而不是观察者本身。我想辩论可观察的
澄清2:我将辩护放在服务方面,因为它是一个单身人士,他们是多个来电者。如果我把它放在呼叫方那边,每个来电者都会有一个不同的去抖定时器。
编辑:这是我试图解释我的问题的片段。只需单击不同的按钮即可查看不同的行为(js代码注释中的更多解释)。Observable.debounce 显示了RxJs的.debounce()如何工作。它只输出' 3'但我想要' 1',' 2',' 3'。
Observable.debounce x3 显示如果我将代码调用3次而不将整个函数包装在去抖动中会发生什么。
Observable wrapped x3 显示我想要获取的内容。我的整个函数都被包装了,但是如果你查看代码,订阅部分就很挑剔了。
Promise x3 显示使用Promise时的简单程度。
let log = (logValue) => {
const list = document.querySelector('#logs');
const li = document.createElement('li');
li.innerHTML = logValue;
list.appendChild(li);
}
/* ************************ */
/* WITH OBSERVABLE.DEBOUNCE */
/* ************************ */
let doStuffObservable = () => {
Rx.Observable.create((observer) => {
log('this should be called only one time (observable.debounce)');
setTimeout(() => {
observer.next('observable.debounce 1');
observer.next('observable.debounce 2');
observer.next('observable.debounce 3');
}, 1000);
})
.debounce(500)
.subscribe((response) => {
log(response);
}, (error) => {
log(error);
});
}
/* *********************************** */
/* WITH OBSERVABLE WRAPPED IN DEBOUNCE */
/* *********************************** */
let doStuffObservable2 = (param) => {
return Rx.Observable.create((observer) => {
log('this should be called only one time (observable wrapped)');
setTimeout(() => {
observer.next('observable wrapped ' + param);
}, 1000);
})
}
let debounceObservable = (callback, delay, immediate) => {
let timeout;
return function () {
let context = this, args = arguments;
return Rx.Observable.create((observer) => {
let later = function () {
timeout = null;
if(!immediate) {
observer.next(callback.apply(context, args));
}
};
let callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, delay);
if(callNow) {
observer.next(callback.apply(context, args));
}
});
}
}
let doStuffObservable2Debounced = debounceObservable(doStuffObservable2);
/* ************* */
/* WITH PROMISES */
/* ************* */
let doStuffPromise = (param) => {
return new Promise((resolve, reject) => {
log('this should be called only one time (promise)');
setTimeout(() => {
resolve('promise ' + param);
}, 1000);
});
}
let debouncePromise = (callback, delay, immediate) => {
let timeout;
return function () {
let context = this, args = arguments;
return new Promise(function (resolve) {
let later = function () {
timeout = null;
if (!immediate) {
resolve(callback.apply(context, args));
}
};
let callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, delay);
if (callNow) {
resolve(callback.apply(context, args));
}
});
}
}
/* ******* */
/* SAMPLES */
/* ******* */
function doObservableDebounce() {
doStuffObservable();
// result :
// this should be called only one time (observable.debounce)
// observable.debounce 3
// this is not what i want, i want all three values in output
}
function doObservableDebounce3Times() {
doStuffObservable();
doStuffObservable();
doStuffObservable();
// result :
// this should be called only one time (observable.debounce)
// this should be called only one time (observable.debounce)
// this should be called only one time (observable.debounce)
// observable.debounce 3
// observable.debounce 3
// observable.debounce 3
// this is bad
}
function doObservableWrappedDebounce3Times() {
doStuffObservable2Debounced(1)
.subscribe((response) => {
log(response);
response.subscribe((response2) => {
log(response2);
}, (error) => {
log(error);
})
}, (error) => {
log(error);
});
doStuffObservable2Debounced(2)
.subscribe((response) => {
log(response);
response.subscribe((response2) => {
log(response2);
}, (error) => {
log(error);
})
}, (error) => {
log(error);
});
doStuffObservable2Debounced(3)
.subscribe((response) => {
log(response);
response.subscribe((response2) => {
log(response2);
}, (error) => {
log(error);
})
}, (error) => {
log(error);
});
// result :
// AnonymousObservable { source: undefined, __subscribe: [Function] }
// this should be called only one time (observable wrapped)
// observable wrapped 3
// this is good but there are 2 embedded subscribe
}
function doPromiseDebounce3Times() {
let doStuffPromiseDebounced = debouncePromise(doStuffPromise);
doStuffPromiseDebounced(1).then(response => {
log(response);
})
doStuffPromiseDebounced(2).then(response => {
log(response);
})
doStuffPromiseDebounced(3).then(response => {
log(response);
})
// result :
// this should be called only one time (promise)
// promise 3
// this is perfect
}

<!DOCTYPE html>
<html>
<head>
<script data-require="rxjs@4.0.6" data-semver="4.0.6" src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.0.6/rx.all.js"></script>
</head>
<body>
<button onclick='doObservableDebounce()'>Observable.debounce</button>
<button onclick='doObservableDebounce3Times()'>Observable.debounce x3</button>
<button onclick='doObservableWrappedDebounce3Times()'>Observable wrapped x3</button>
<button onclick='doPromiseDebounce3Times()'>Promise x3</button>
<ul id="logs"></ul>
</body>
</html>
&#13;
答案 0 :(得分:2)
抱歉,我没有收到您对我的评论的回复通知。
针对此问题的更干净的仅限Rx的解决方案是将您的服务调用视为事件流,如下所示:
constructor() {
this._genericServiceCall$ = new ReplaySubject(1);
this._genericServiceResult$ = this._genericServiceCall$
.asObservable()
.debounceTime(1000)
.switchMap(params => this._genericService(params));
}
private _genericService(params) {
//Do some stuff
//...
return this.http.post('http://foo.com', params)
.map((response) => {
//Do some generic stuff
//...
return someData;
})
.catch((error: any) => {
//Manage error in a generic way + do some generic stuff
//...
return Observable.throw(error);
});
}
public genericService(params) {
this._genericServiceCall$.next(params);
return this._genericServiceResult$; // Optionally add `.take(1)` so the observer has the expected behaviour of only getting 1 answer back
}
我在这看到了一些内容...... params
你会接受那些必须通过私人_genericService
的人吗?
无论如何,你是否关注这里发生的事情?因此,每当有人拨打genericService()
时,它都不会立即调用服务 - 相反,它会发出新的_genericServiceCall$
并返回_genericServiceResult$
信息流。如果我们看看这个流是如何定义的,我们会看到它需要去抖动_genericServiceCall$
,然后将其映射到服务调用。从理论上说它应该有效 - 没试过。
编辑:现在我明白了 - 您可能需要发布genericServiceResult以使其成为热门观察者,否则只要观察者订阅它就会返回:
constructor() {
this._genericServiceCall$ = new ReplaySubject(1);
this._genericServiceResult$ = this._genericServiceCall$
.asObservable()
.debounceTime(1000)
.switchMap(params => this._genericService(params))
.publish();
const subscription = this._genericServiceResult$.connect();
// You must store subscription somewhere and dispose it when this object is destroyed - If it's a singleton service this might not be needed.
}
答案 1 :(得分:0)
好的,我想我找到了办法。我应该做的是替换:
observer.next(callback.apply(context, args));
通过
callback.apply(context, args).subscribe((response) => {
observer.next(response)
}, (error) => {
observer.error(error);
});
最后,这可以像经典的observable一样使用:
debouncedObservable(1)
.subscribe((response) => {
log(response);
}, (error) => {
log(error);
});
以下是实施的摘录:
let log = (logValue) => {
const list = document.querySelector('#logs');
const li = document.createElement('li');
li.innerHTML = logValue;
list.appendChild(li);
}
/* *********************************** */
/* WITH OBSERVABLE WRAPPED IN DEBOUNCE */
/* *********************************** */
let doStuffObservable = (param) => {
return Rx.Observable.create((observer) => {
log('this should be called only one time (observable wrapped)');
setTimeout(() => {
observer.next('observable wrapped ' + param);
}, 1000);
})
}
let debounceObservable = (callback, delay, immediate) => {
let timeout;
return function () {
let context = this, args = arguments;
return Rx.Observable.create((observer) => {
let later = function () {
timeout = null;
if(!immediate) {
callback.apply(context, args).subscribe((response) => {
observer.next(response)
}, (error) => {
observer.error(error);
});
}
};
let callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, delay);
if(callNow) {
callback.apply(context, args).subscribe((response) => {
observer.next(response)
}, (error) => {
observer.error(error);
});
}
});
}
}
let doStuffObservable2Debounced = debounceObservable(doStuffObservable);
/* ******* */
/* SAMPLES */
/* ******* */
function doObservableWrappedDebounce3Times() {
doStuffObservable2Debounced(1)
.subscribe((response) => {
log(response);
}, (error) => {
log(error);
});
doStuffObservable2Debounced(2)
.subscribe((response) => {
log(response);
}, (error) => {
log(error);
});
doStuffObservable2Debounced(3)
.subscribe((response) => {
log(response);
}, (error) => {
log(error);
});
}
<!DOCTYPE html>
<html>
<head>
<script data-require="rxjs@4.0.6" data-semver="4.0.6" src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.0.6/rx.all.js"></script>
</head>
<body>
<button onclick='doObservableWrappedDebounce3Times()'>Observable wrapped x3</button>
<ul id="logs"></ul>
</body>
</html>
如果您认为我遗漏了某些内容,请发表评论。