I don't know why subscription.unsubscribe is not a function.
I can't figure out.
const Rx = require("rx");
const observable = Rx.Observable.create(function(observer) {
observer.next(1);
observer.next(2);
const intervalId = setInterval(() => {
observer.next('nmb');
}, 1000);
return function unsubscribe() {
clearInterval(intervalId);
}
});
const subscription = observable.subscribe(x => console.log(x));
setTimeout(() => {
subscription.unsubscribe();
}, 5000);
This issue is encountered when writing code against the rxjs 5 api but referencing rxjs 4.
Related
The custom implementation below of Promise.race is working correctly when I pass a Promise object to it. However, if I pass Promise.reject it sort of dismisses it and just resolves to the Promise.resolve value.
function race(promises) {
return new Promise((resolve, reject) => {
function resolveCB(value) {
resolve(value);
}
function rejectCB(value) {
reject(value);
}
promises.forEach((p) => {
p.then(resolveCB).catch(rejectCB);
});
});
}
const p1 = Promise.reject(1)
const p2 = Promise.reject(5)
const p3 = Promise.resolve(2)
race([p1, p2, p3]).then(result => console.log(result)).catch(err => console.log(err));
The above code logs 2. I would expect it to log 1.
const p1 = new Promise((resolve, reject) => {
setTimeout(() => reject(1), 1500);
});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => resolve(6), 1000);
});
const p3 = new Promise((resolve, reject) => {
setTimeout(() => reject(8), 500);
});
This logs 8 as expected, so the issue is not in the function race it seems but in the misunderstanding of Promise.reject I guess?
If you switch the order of then and catch in your forEach, you can see the results you're expecting. The then takes up the space in the microtask queue even though the Promise rejection and lack of a catch handler means it doesn't actually transform the promise.
promises.forEach((p) => {
// This demonstrates how the results are sensitive to the order, but
// creates the opposite ordering: rejected promises are favored
// over resolved ones.
p.catch(rejectCB).then(resolveCB);
});
function race(promises) {
return new Promise((resolve, reject) => {
function resolveCB(value) {
resolve(value);
}
function rejectCB(value) {
reject(value);
}
promises.forEach((p) => {
p.catch(rejectCB).then(resolveCB);
});
});
}
const p1 = Promise.reject(1)
const p2 = Promise.reject(5)
const p3 = Promise.resolve(2)
race([p1, p2, p3]).then(result => console.log(result)).catch(err => console.log(err));
It'd be better, though, just to use the normal two-arg behavior of then so both of those outcomes happen at the same time:
promises.forEach((p) => {
p.then(resolveCB, rejectCB);
});
function race(promises) {
return new Promise((resolve, reject) => {
function resolveCB(value) {
resolve(value);
}
function rejectCB(value) {
reject(value);
}
promises.forEach((p) => {
p.then(resolveCB, rejectCB);
});
});
}
const p1 = Promise.reject(1)
const p2 = Promise.reject(5)
const p3 = Promise.resolve(2)
race([p1, p2, p3]).then(result => console.log(result)).catch(err => console.log(err));
(Outside of a toy or homework problem, you should be using the built-in race or a common polyfill. FWIW, the core-js polyfill uses the two-arg then.)
Preconditions:
The ref.getDownload() returns an Observable which only can be subscribed, if the
task.snapshotChanges()-Observable completed.
This code-snippet works:
task.snapshotChanges().subscribe({
complete: () => {
ref.getDownloadURL().subscribe((downloadUrl) => console.log(downloadUrl));
}
});
This code-snippet does NOT work:
concat(
task.snapshotChanges(),
ref.getDownloadURL()
).pipe(
last()
).subscribe((downloadUrl) => console.log(downloadUrl));
getDownloadUrl throws an error (404 file not found), because it seems
ref.getDownloadUrl is subscribed to early.
Why subscribes the ref.getDownloaded()-Observable and does not wait until task.snapshotChanges() completes? The concat-operator should ensure this behaviour.
Or am I wrong?
The function ref.getDownloadURL() is called when the concat(..) Observable is created. See:
const { of, concat } = rxjs;
const { delay } = rxjs.operators;
const fetch1 = () => { console.log('run fetch1'); return of('from 1').pipe(delay(2000)) }
const fetch2 = () => { console.log('run fetch2'); return of('from 2').pipe(delay(2000)) }
concat(fetch1(), fetch2()).subscribe(console.log);
<script src="https://unpkg.com/rxjs/bundles/rxjs.umd.min.js"></script>
ref.getDownloadURL() seems to query the database directly when it gets called and not when the Observable it returns gets subscribed to.
You can wrap ref.getDownloadURL() with defer to only execute it when the Observable is subscribed to.
const { of, concat, defer } = rxjs;
const { delay } = rxjs.operators;
const fetch1 = () => { console.log('run fetch1'); return of('from 1').pipe(delay(2000)) }
const fetch2 = () => { console.log('run fetch2'); return of('from 2').pipe(delay(2000)) }
concat(fetch1(), defer(() => fetch2())).subscribe(console.log);
<script src="https://unpkg.com/rxjs/bundles/rxjs.umd.min.js"></script>
Also see my answer here https://stackoverflow.com/a/57671521/9423231
I want the done to print only after the first and second is printed.
const obs1 = new Observable<any>((observer) => {
setTimeout(() => {
console.log('first');
observer.next();
observer.complete();
}, 10000);
});
const obs2 = new Observable<any>((observer) => {
setTimeout(() => {
console.log('second');
observer.next();
observer.complete();
}, 1000);
});
from([obs1, obs2]).pipe(concatAll()).subscribe(() => {
console.log('done');
});
You don't complete any of the two source Observables so no operator can know what you consider as "done". This means you could use merge or combineLatest and only handle next notifications.
However, if you know they'll always emit just once you can complete each source and then use forkJoin or concat:
const obs1 = new Observable<any>((observer) => {
setTimeout(() => {
console.log('first');
observer.next();
observer.complete();
}, 10000);
});
...
concat(obs1, obs2).subscribe({
complete: () => {
console.log('done');
}
});
I define an Observable like this:
const obs$ = Observable.create(...)
.publishReplay(1)
.refCount();
So that it puts a ReplaySubject(1) between my source Observable and all observers.
Since ReplaySubject has in its state the number of observers (via its observers array property), how is it possible to access the ReplaySubject from obs$?
I actually only need to know if obs$ has any observers or not. RxJS4 had a hasObservers() method on Subject, but it got removed in RxJS5. How can I achieve this with RxJS5?
Not sure about your usage but for my needs I created a custom operator that allowed me to transparently perform side-effects (similar to tap) based on the state of the refCount. It just does a pass-through subscription and duck-punches the sub/unsub. The callback gets the current refCount and the previous so that you can tell the state and direction. I like using an operator for this since I can insert it at any point in my stream. If you simply want a binary output for whether there are any subscriptions or not it could be easily modified for that.
const { Observable, Observer, interval } = rxjs;
const { publishReplay, refCount } = rxjs.operators;
const tapRefCount = (onChange) => (source) => {
let refCount = 0;
// mute the operator if it has nothing to do
if (typeof onChange !== 'function') {
return source;
}
// mute errors from side-effects
const safeOnChange = (refCount, prevRefCount) => {
try {
onChange(refCount, prevRefCount);
} catch (e) {
}
};
// spy on subscribe
return Observable.create((observer) => {
const subscription = source.subscribe(observer);
const prevRefCount = refCount;
refCount++;
safeOnChange(refCount, prevRefCount);
// spy on unsubscribe
return () => {
subscription.unsubscribe();
const prevRefCount = refCount;
refCount--;
safeOnChange(refCount, prevRefCount);
};
});
};
const source = interval(1000).pipe(
publishReplay(1),
refCount(),
tapRefCount((refCount, prevRefCount) => { console.log('refCount', refCount, prevRefCount > refCount ? 'down': 'up'); })
);
const firstSub = source.subscribe((x) => { console.log('first', x); });
let secondSub;
setTimeout(() => {
secondSub = source.subscribe((x) => { console.log('second', x); });
}, 1500);
setTimeout(() => {
firstSub.unsubscribe();
}, 4500);
setTimeout(() => {
secondSub.unsubscribe();
}, 5500);
<script src="https://unpkg.com/rxjs#rc/bundles/rxjs.umd.min.js"></script>
The typescript version:
import { Observable } from 'rxjs/Observable';
import { Observer } from 'rxjs/Observer';
export const tapRefCount = (
onChange: (refCount: number, prevRefCount: number) => void
) => <T>(source: Observable<T>): Observable<T> => {
let refCount = 0;
// mute the operator if it has nothing to do
if (typeof onChange !== 'function') {
return source;
}
// mute errors from side-effects
const safeOnChange = (refCount, prevRefCount) => {
try {
onChange(refCount, prevRefCount);
} catch (e) {
}
};
// spy on subscribe
return Observable.create((observer: Observer<T>) => {
const subscription = source.subscribe(observer);
const prevRefCount = refCount;
refCount++;
safeOnChange(refCount, prevRefCount);
// spy on unsubscribe
return () => {
subscription.unsubscribe();
const prevRefCount = refCount;
refCount--;
safeOnChange(refCount, prevRefCount);
};
}) as Observable<T>;
};
The Subject class has a public property called observers (see https://github.com/ReactiveX/rxjs/blob/5.5.10/src/Subject.ts#L28)
So you can use just:
const s = new Subject();
...
if (s.observers.length > 0) {
// whatever
}
Be aware that refCount returns an Observable so you won't be able to do what I mentioned above. However, you can provide your own Subject instance to publishReplay as the third argument and use s.observers on that, see http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-publishReplay
I have a function that returns promise:
Setup.zoomIn() : Promise<void> {...}
I would like to use rxjs to invoke that function 5 times with delay of 1 second between each, like this:
let obs = Observable.create(observer => {
let count = 0;
setTimeout(() => {
Setup.zoomIn();
count++;
observer.next();
}, 1000);
if (count === 5) {observer.complete();}
};
obs.subscribe(() =>
console.log('zoomed out');
)};
Only and only when that is executed I would like to continue with execution to perform further steps:
obs.toPromise.then(() => {
// do some stuff here but only after zoom has been invoked 5 times
})
Create a list of observables for zoomIns functions and concat them with another Observable.
function zoomIn(i) {
return new Promise(res => {
setTimeout(()=>res(i), 1000);
});
};
function anotherPromise() {
return Rx.Observable.defer(()=> {
return new Promise(res => {
setTimeout(()=>res('anotherPromise'), 3000);
});
});
}
const zoonInList = Array(5).fill(0).map((x, i)=>i).map(i=>
Rx.Observable.defer(()=> {
return zoomIn(i);
})
);
Rx.Observable.concat(...zoonInList, anotherPromise())
.subscribe(x=>console.log(x))