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');
}
});
In this code why subject.onNext(3) is printing first as I set subject.sample(500) but setTimeout(200)?
const Rx = require('rx');
const subject = new Rx.Subject();
const sampleObservable = subject.sample(500);
sampleObservable.subscribe(
data => console.log(data),
error => console.log(error),
() => console.log('FINISHED')
);
subject.onNext(0);
subject.onNext(1);
setTimeout(() => {
subject.onNext(2);
subject.onNext(3);
subject.onCompleted();
}, 200);
Problem: Game: So I have some ships that can arrive to many planets. If the 2 ships arrive at the same time on the new planet can lead to the same process of changing ownership twice. This process is asynchronous and should only happen once per planet ownership change.
To fix this I want split the stream of ships by planet id so each stream will be for only one planet. Now the tricky part is that each ship should only be processed after the previous one has been processed.
Ships$
Split by planet id
planet id1: process in sequence
planet id2: process in sequence
...
Here is some code that will show how it should behave.
const ships = [
{
id: 1,
planetId: 1,
},
{
id: 2,
planetId: 1,
},
{
id: 3,
planetId: 2,
},
// ... never finishes
]
// the source observable never finishes
const source$ = interval(1000).pipe(
take(ships.length),
map(i => ships[i]),
)
const createSubject = (ship) => {
// Doesn't need to be a subject, but needs to emit new items after a bit of time based on some other requests.
console.log(`>>>`, ship.id);
const subject = new Subject();
setTimeout(() => {
subject.next(ship.id + ' a' + new Date());
}, 1000);
setTimeout(() => {
subject.next(ship.id + ' b' + new Date());
subject.complete();
}, 2000);
return subject.asObservable();
}
// The result should be the following (t, is the time in seconds, t3, is time after 3 seconds)
// t0: >>> 1
// t0: >>> 3
// t1: 1 a
// t1: 2 a
// t2: 1 b
// t2: 2 b
// t2: >>> 2 (note that the second ship didn't call the createSubject until the first finished)
// t3: 1 a
// t4: 1 2
Solution (with a lot of help from A.Winnen and some figuring out)
Run it here: https://stackblitz.com/edit/angular-8zopfk?file=src/app/app.component.ts
const ships = [
{
id: 1,
planetId: 1,
},
{
id: 2,
planetId: 1,
},
{
id: 3,
planetId: 2,
}
];
const createSubject = (ship) => {
console.log(ship.id + ' a')
const subject = new Subject();
setTimeout(() => {
//subject.next(ship.id + ' b');
}, 500);//
setTimeout(() => {
subject.next(ship.id + ' c');
subject.complete();//
}, 1000);
return subject.asObservable();
}
let x = 0;
interval(10).pipe(//
take(ships.length),
map(i => ships[i]),
groupBy(s => s.planetId),
mergeMap(group$ => {//
x++
return group$.pipe(
tap(i => console.log('x', i, x)),
concatMap(createSubject)
)
}),
).subscribe(res => console.log('finish', res), undefined, () => console.log("completed"))
How can this be done in rxjs?
Code:
const shipArriveAction$ = action$.pipe<AppAction>(
ofType(ShipActions.arrive),
groupBy(action => action.payload.ship.toPlanetId),
mergeMap((shipByPlanet$: Observable<ShipActions.Arrive>) => {
return shipByPlanet$.pipe(
groupBy(action => action.payload.ship.id),
mergeMap((planet$) => {
return planet$.pipe(
concatMap((action) => {
console.log(`>>>concat`, new Date(), action);
// this code should be called in sequence for each ship with the same planet. I don't need only the results to be in order, but also this to be called in sequence.
const subject = new Subject();
const pushAction: PushAction = (pushedAction) => {
subject.next(pushedAction);
};
onShipArriveAction(state$.value, action, pushAction).then(() => {
subject.complete();
});
return subject.asObservable();
}),
)
})
);
)
;
The code from A.Winnen is very close, but only works with a source observable that is finished, not continuous:
const ships = [
{
id: 1,
planetId: 1,
},
{
id: 2,
planetId: 1,
},
{
id: 3,
planetId: 2,
}
];
const createSubject = (ship) => {
console.log(ship.id + ' a')
const subject = new Subject();
setTimeout(() => {
subject.next(ship.id + ' b');
}, 1000);//
setTimeout(() => {
subject.next(ship.id + ' c');
subject.complete();//
}, 2000);
return subject.asObservable().pipe(
finalize(null)
);
}
interval(1000).pipe(
take(ships.length),
tap(console.log),
map(i => ships[i]),
groupBy(s => s.planetId),
mergeMap(group => group.pipe(toArray())),
mergeMap(group => from(group).pipe(
concatMap(createSubject)
))
).subscribe(res => console.log(res), undefined, () => console.log("completed"))
you can use a combination of groupBy and mergeMap to achieve your goal.
from(ships).pipe(
groupBy(ship => ship.planetId),
mergeMap(planetGroup => planetGroup.pipe(
concatMap(ship => {
// do real processing in this step
return of(`planetGroup: ${planetGroup.key} - processed ${ship.ship}`);
})
))
).subscribe(result => console.log(result));
I made a simple example: https://stackblitz.com/edit/angular-6etaja?file=src%2Fapp%2Fapp.component.ts
EDIT:
updated blitzstack: https://stackblitz.com/edit/angular-y7znvk
I'm new to RxJ.
The version of RxJS 6.2.
I'm trying fromEvent method and filtering out mouse event info but it shows mouse position data even when clientX is bigger than 500 and seems filter method is not working.
Could anyone tell me what I'm doing wrong and how to fix it?
JavaScript
import { Observable, fromEvent } from "rxjs";
import { map, filter } from 'rxjs/operators';
let numbers = [1, 5, 10];
const source = fromEvent(document, "mousemove");
source.pipe(
map((e: MouseEvent) => {
return {
x: e.clientX,
y: e.clientY
}
}),
filter(value => value.x < 500)
)
source.subscribe(
value => console.log(value),
e => console.log(`error: ${e}`),
() => console.log('complete')
);
The call to pipe returns a new observable - which you are ignoring. It does not modify the source observable.
You should subscribe to the observable returned by pipe instead of the source observable:
const source = fromEvent(document, "mousemove");
const mappedAndFiltered = source.pipe(
map((e: MouseEvent) => {
return {
x: e.clientX,
y: e.clientY
};
}),
filter(value => value.x < 500)
);
mappedAndFiltered.subscribe(
value => console.log(value),
e => console.log(`error: ${e}`),
() => console.log('complete')
);
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.