This observable polls getPromise() function every second. After getPromise() function returns 3 promises it stops resolving them. How do I detect that getPromise() function hasn't resolve/rejected any promise for the past, let's say, 2 seconds, and call onError handler. I've tried making it work with timeout operator to no avail. Any ideas?
Rx.Observable.interval(1000)
.switchMap(() => Rx.Observable.fromPromise(getPromise()))
.subscribe(onValue, onError);
function onValue(value){
console.log('value: ', value);
}
function onError(error){
console.log('error: ', error);
}
var getPromise = (function(){
var counter = 3;
return function(){
return new Promise(function(resolve, reject){
if(counter > 0) resolve(1);
counter--;
})
}
})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.3.0/Rx.js"></script>
You can use the race operator that subscribes only to the first Observable that emits.
You said you want to call onError handler after 2 of inactivity. This contradicts with using switchMap which automatically unsubscribes when a new Observable is returned from its callback. So you might want to use exhaustMap instead. Also when you emit an error notification the chain unsubscribes and you'll never receive any other value. This means that you shouldn't emit the timeout as an error or use also the retry operator to automatically resubscribe (but this really depends on what you're trying to achieve).
This is you updated example that is just using the race() operator.
Rx.Observable.interval(1000)
.switchMap(() =>
Rx.Observable.race(
Rx.Observable.fromPromise(getPromise()),
Rx.Observable.timer(0, 1000).mapTo(42)
)
)
.subscribe(onValue, onError);
function onValue(value){
console.log('value: ', value);
}
function onError(error){
console.log('error: ', error);
}
var getPromise = (function(){
var counter = 3;
return function(){
return new Promise(function(resolve, reject){
if(counter > 0) resolve(1);
counter--;
})
}
})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.3.0/Rx.js"></script>
Edit: To send a single error notification after 2 seconds of inactivity.
Rx.Observable.interval(1000)
.switchMap(() => Rx.Observable.fromPromise(getPromise()))
.timeout(2000)
.subscribe(onValue, onError);
function onValue(value){
console.log('value: ', value);
}
function onError(error){
console.log('error: ', error);
}
var getPromise = (function(){
var counter = 3;
return function(){
return new Promise(function(resolve, reject){
if(counter > 0) resolve(1);
counter--;
})
}
})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.2.0/Rx.js"></script>
There's really a bug in 5.3.0 not directly in the timeout() operator but in scheduling async actions. https://github.com/ReactiveX/rxjs/pull/2580
Without the timeout() operator:
Rx.Observable.interval(1000)
.switchMap(() =>
Rx.Observable.race(
Rx.Observable.fromPromise(getPromise()),
Rx.Observable.timer(0, 2000).map(function(_) {
throw new Error('timeout');
})
)
)
.subscribe(onValue, onError);
Related
I am in the middle of upgrading my Parse-server 2.0 Cloud functions to 3.0.
One of the functions working in 2.0 is:
Parse.Cloud.define("countOfObservations", function(request, response) {
var query = new Parse.Query("GCUR_OBSERVATION");
query.count({
success: function(count) {
// The count request succeeded. Show the count
response.success(count);
},
error: function(error) {
response.error("GCUR_OBSERVATION table lookup failed");
}
});
});
As the Parse-server 3.0 has removed response and leveraged native promises or async/await for asynchronous validation. I tried to rewrite the function as below.
Parse.Cloud.define("countOfObservations", (request) => {
var query = new Parse.Query("GCUR_OBSERVATION");
var countOfObs = 0;
query.count({ useMasterKey: true }).then( (count) => {
countOfObs = count;
console.log("*** count=" + countOfObs);
return new Promise(function(resolve, reject) {
if (countOfObs >= 0)
return resolve(countOfObs);
else
return reject();
})
});
});
When I called the function from client, it returned {} instead of {"result":2882} that I'd expected. However, the console did print *** count=2882.
How can I make the function work again using native promises?
The reason it returns an empty object is because your function does not wait for query.count to complete and immediately returns with undefined.
You can write return await query.count(...).then(...); or even better, transform the whole function into Promise syntax and save some code lines. The Parse Team has developed a helpful migration guide with examples to make the transition easier for you.
Then this would be all you need:
Parse.Cloud.define("countOfObservations", async (request) => {
const query = new Parse.Query("GCUR_OBSERVATION");
const count = await query.count({ useMasterKey: true });
console.log("*** count=" + count);
return {"count": count};
// or if you really need to reject:
// return count >= 0 ? Promise.resolve(count) : Promise.reject();
});
I have a Subject that maps an observable, I subscribe to the observable in the concatMap but it doesn't trigger tap() from the subscription.
this.streamA$ = this.streamService.getStream(1)
.pipe(
tap(data => console.log('stream data:', data))
);
Subject
this.images$ = this.queue.pipe(concatMap((event: Observable<string>) => {
// when an event arrives here it is still wrapped in observable
// subscribe isn't triggering tap()
event.subscribe(data => {
//console.log('inner observable subscription:', data);
});
// observable goes to the image$ observable, it is unwrapped by: image$ | async in template
return event;
}));
Service function
getStream(time: number): Observable<string> {
let timer$ = timer(2000);
console.log('get stream');
const observable = new Observable<string>(observer => {
timer$.pipe(
map(() => {
observer.next('http response 1');
observer.complete();
})
).subscribe();
});
return observable;
}
Update:
event.subscribe(data => {
//console.log('inner observable subscription:', data);
});
Without console.log included in subscribe this is the output:
stream data: http response 1
With console.log, prints these 3 lines at the same time:
stream data: http response 1
inner observable subscription: http response 1
stream data: http response 1
When you have an Observable inside a concatMap, you don't need to subscribe. Could you try something along these lines?
this.images$ = this.queue.pipe(
concatMap(event => event), // I assume the event here is something like this.streamA$
tap(data => // do stuff with data)
);
I am having some problem in vuejs in executing a function/method sequentially.
I have three functions like:
MethodA: function(){
if(x = 1){
value1 = 2;
}
if (x ==2){
value2 = 4;
}
this.MethodB();
}
MethodB: function(){
Total value = value1 + value2;
}
MethodC: function (){
this.$http.get('api/getvalue').then(function(response){
this.set('somedata', response.data);
response.data.forEach(para){
if(para.id == 1){
this.MethodA();
}
if(para.id == 2){
this.MethodA();
}
}
});
}
ready: function(){
this.MethodC();
}
I would like to execute this.MethodB() only after MethodC and MethodA has completely executed. How can I do this?
You can use Javascript Promises with Vue.js methods:
methodA: function() {
return new Promise(function(resolve, reject) {
//run all your methodA code here
...
resolve('MethodA finished');
});
},
methodB: function() {
return new Promise(function(resolve, reject) {
//run all your methodB code here
...
resolve('MethodB finished');
});
},
methodC: function() {
//run your methodC code
}
Now, to run methodC only when methodA and methodB are finished, you can use the promises .then and chain them together. For ex:
ready: function() {
//save `this` to a variable just to make it easier to be accessed within the chain
let self = this;
//run methodA, then methodB...only then, methodC
self.methodA.then(function(resultA) {
console.log(resultA);
return self.methodB();
}).then(function(resultB) {
console.log(resultB);
self.methodC();
});
}
Note: if you running AJAX calls within methodA or methodB, make sure to resolve the promise only when you receive a response. In your ex.:
this.$http.get('api/getvalue').then(function(response){
...
resolve('My method is now complete');
}
I'm quite new at rxjs stuff so please be patience :).
var source = Rx.Observable.fromEvent(document, 'keyup');
source.filter(function(x){
console.log('filter with', x);
return true;
});
var subscription = source.subscribe(
function (x) {
console.log('Next: keyup!',x.keyCode);
},
function (err) {
console.log('Error: %s', err);
},
function () {
console.log('Completed');
});
whats the right way to debug inside filter
I dont see any filter with
in the console
I've also tried with
var source = Rx.Observable.fromEvent(document, 'keyup');
source.filter(function(x){
console.log('filter with', x);
return true;
});
source.do(x => console.log('do with',x));
var subscription = source.subscribe(
function (x) {
console.log('Next: keyup!',x.keyCode);
},
function (err) {
console.log('Error: %s', err);
},
function () {
console.log('Completed');
});
with no lucky
Can you give me an hint please ?
source.filter() is creating a new Observable, yet you only subscribe to the original Observable, source. Observables that aren't subscribed to are not carried out
You have to do something like this:
source.filter()
.do()
.subscribe()
I'm trying to detect if the mousedown event is held for a period of time before a mouseup.
I'm using timeout() on an Observable created with fromEvent() to do so, but the timeout returns both Observables.
Below, subscribing to stream returns the event if mousedown is triggered within 1 second, but it also returns 1.
var mousedown = Rx.Observable.fromEvent(target, 'mousedown');
var stream = mousedown.timeout(1000, Rx.Observable.return(1));
var sub = stream.subscribe(
function (x) {
console.log('Next: '+x);
},
function (err) {
console.log('Err: '+err);
},
function () {
console.log('Complete');
}
);
However, this works as expected:
var source = Rx.Observable.return(42)
.delay(200)
.timeout(1000, Rx.Observable.return(1));
I'd like this code to work:
var mousedown = Rx.Observable.fromEvent(target, 'mousedown');
var mouseup = Rx.Observable.fromEvent(target, 'mouseup');
var clickhold = mousedown
.flatMap(function (){
return mouseup.timeout(1000, Rx.Observable.return('hold'));
})
.filter(function (x) {
return x === 'hold';
});
clickhold.subscribe(
function (x) {
console.log('Next: '+x);
},
function (err) {
console.log('Err: '+err);
},
function () {
console.log('Complete');
}
);
Instead of using timeout, I used delay and takeUntil:
var target,
mousedown,
mouseup;
target = document.querySelector('input');
mousedown = Rx.Observable.fromEvent(target, 'mousedown');
mouseup = Rx.Observable.fromEvent(target, 'mouseup');
var clickhold = mousedown
.flatMap(function(){
// Triggered instantly after mousedown event.
return Rx.Observable
.return('hold')
.delay(1000)
// Discards the return value if by the time .delay() is complete
// mouseup event has been already completed.
.takeUntil(mouseup);
});
clickhold.subscribe(
function (x) {
console.log('Next: ' + x);
},
function (err) {
console.log('Err: ' + err);
},
function () {
console.log('Complete');
}
);
<script src='https://rawgit.com/Reactive-Extensions/RxJS/v.2.5.3/dist/rx.all.js'></script>
<input type='button' value='Button' />
You came up with a great solution on your own. Here's what I would change:
Move the inner observable (timer(...).takeUntil(...).select(...)) out of flatMap, so it isn't re-allocated for each mouse down.
You've got the rest right. For my usage, I usually retain the original mousedown event and use that instead of 'hold'. That requires returnValue and delay instead of timer and select.
var target,
mousedown,
mouseup;
target = document.querySelector('input');
mousedown = Rx.Observable.fromEvent(target, 'mousedown');
mouseup = Rx.Observable.fromEvent(target, 'mouseup');
var clickhold = mousedown
.flatMap(function (e) {
return Rx.Observable
.return(e)
.delay(1000)
.takeUntil(mouseup);
});
clickhold.subscribe(function (x) {
console.log('onNext: ', x);
});
<script src='https://rawgit.com/Reactive-Extensions/RxJS/v.2.5.3/dist/rx.all.js'></script>
<input type='button' value='Button' />
Or, for a completely different approach...
var Observable = Rx.Observable,
fromEvent = Observable.fromEvent.bind(Observable, target),
holdDelay = Observable.empty().delay(1000);
Observable
.merge(
[
fromEvent('mouseup')
.map(empty),
fromEvent('mousedown')
.map(Observable.returnValue)
.map(holdDelay.concat.bind(holdDelay))
]
)
.switchLatest();
Ok so that's weird. I'm really just giving it as food for though, and to show off that this can be done in a number of different ways.