Concat observables reusing the previous results
Imagine I have 3 functions, taking some arguments and returning an observable. Logic would be (all would be a result of something async):
- Get a cat.
- Pet the cat.
- Get food for the cat based on it's mood after petting it.
Sample code:
function getCat(): Observable<Cat> {
return Observable.of({ name: 'Larry' })
}
function petCat(cat: Cat): Observable<Mood> {
return Observable.of(Mood.HAPPY)
}
function getFood(cat: Cat, mood: Mood): Observable<Food> {
return Observable.of({ type: 'fish' })
}
I would like to end up with an Observable that's equivalent with this:
Observable.from([{ name: 'Larry' }, Mood.HAPPY, { type: 'fish' }])
I would like the functions to be invoked in sequence (obviously from the parameter signatures)
- I would not like to delay the individual emits, in the final observable I'd like to get the results as soon as they arrive.
I could get it working with something like this:
function perform() {
return getCat().pipe(
mergeMap((cat) => concat(
of(cat),
petCat(cat).pipe(
mergeMap((mood) => concat(
of(mood),
getFood(cat, mood)
))
)
)
),
)
}
Question is, is there a better, more readable way of doing this (let's say I would have 5 of these that I would like to chain)?
javascript typescript rxjs observable
add a comment |
Imagine I have 3 functions, taking some arguments and returning an observable. Logic would be (all would be a result of something async):
- Get a cat.
- Pet the cat.
- Get food for the cat based on it's mood after petting it.
Sample code:
function getCat(): Observable<Cat> {
return Observable.of({ name: 'Larry' })
}
function petCat(cat: Cat): Observable<Mood> {
return Observable.of(Mood.HAPPY)
}
function getFood(cat: Cat, mood: Mood): Observable<Food> {
return Observable.of({ type: 'fish' })
}
I would like to end up with an Observable that's equivalent with this:
Observable.from([{ name: 'Larry' }, Mood.HAPPY, { type: 'fish' }])
I would like the functions to be invoked in sequence (obviously from the parameter signatures)
- I would not like to delay the individual emits, in the final observable I'd like to get the results as soon as they arrive.
I could get it working with something like this:
function perform() {
return getCat().pipe(
mergeMap((cat) => concat(
of(cat),
petCat(cat).pipe(
mergeMap((mood) => concat(
of(mood),
getFood(cat, mood)
))
)
)
),
)
}
Question is, is there a better, more readable way of doing this (let's say I would have 5 of these that I would like to chain)?
javascript typescript rxjs observable
add a comment |
Imagine I have 3 functions, taking some arguments and returning an observable. Logic would be (all would be a result of something async):
- Get a cat.
- Pet the cat.
- Get food for the cat based on it's mood after petting it.
Sample code:
function getCat(): Observable<Cat> {
return Observable.of({ name: 'Larry' })
}
function petCat(cat: Cat): Observable<Mood> {
return Observable.of(Mood.HAPPY)
}
function getFood(cat: Cat, mood: Mood): Observable<Food> {
return Observable.of({ type: 'fish' })
}
I would like to end up with an Observable that's equivalent with this:
Observable.from([{ name: 'Larry' }, Mood.HAPPY, { type: 'fish' }])
I would like the functions to be invoked in sequence (obviously from the parameter signatures)
- I would not like to delay the individual emits, in the final observable I'd like to get the results as soon as they arrive.
I could get it working with something like this:
function perform() {
return getCat().pipe(
mergeMap((cat) => concat(
of(cat),
petCat(cat).pipe(
mergeMap((mood) => concat(
of(mood),
getFood(cat, mood)
))
)
)
),
)
}
Question is, is there a better, more readable way of doing this (let's say I would have 5 of these that I would like to chain)?
javascript typescript rxjs observable
Imagine I have 3 functions, taking some arguments and returning an observable. Logic would be (all would be a result of something async):
- Get a cat.
- Pet the cat.
- Get food for the cat based on it's mood after petting it.
Sample code:
function getCat(): Observable<Cat> {
return Observable.of({ name: 'Larry' })
}
function petCat(cat: Cat): Observable<Mood> {
return Observable.of(Mood.HAPPY)
}
function getFood(cat: Cat, mood: Mood): Observable<Food> {
return Observable.of({ type: 'fish' })
}
I would like to end up with an Observable that's equivalent with this:
Observable.from([{ name: 'Larry' }, Mood.HAPPY, { type: 'fish' }])
I would like the functions to be invoked in sequence (obviously from the parameter signatures)
- I would not like to delay the individual emits, in the final observable I'd like to get the results as soon as they arrive.
I could get it working with something like this:
function perform() {
return getCat().pipe(
mergeMap((cat) => concat(
of(cat),
petCat(cat).pipe(
mergeMap((mood) => concat(
of(mood),
getFood(cat, mood)
))
)
)
),
)
}
Question is, is there a better, more readable way of doing this (let's say I would have 5 of these that I would like to chain)?
javascript typescript rxjs observable
javascript typescript rxjs observable
edited Nov 14 '18 at 17:50
Balázs Édes
asked Nov 14 '18 at 17:29
Balázs ÉdesBalázs Édes
9,31723258
9,31723258
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
You can try something like
getCat()
.pipe(
switchMap(cat => petCat(cat).pipe(map(mood => ({cat, mood})))),
switchMap(({cat, mood}) => getFood(cat, mood))
)
The whole idea is to use switchMap
to switch from the source Observable to the Observable returned by the function passed to switchMap
as parameter.
Maybe worth of mentioning is the use of map
chained into the pipe of the first switchMap
. That map
operator makes sure that we keep cat
as part of the parameters we pass the second, and last, switchMap
.
UPDATE after comment
If the function perform
has to emit all the 3 elements emitted by the 3 functions, then you may try something like the following
function perform() {
return getCat()
.pipe(
switchMap(cat => petCat(cat).pipe(map(mood => ({cat, mood})))),
switchMap(({cat, mood}) => getFood(cat, mood).pipe(map(food => ({cat, mood, food}))))
)
}
Hey @Picci, thanks for the answer. The problem with this answer, is that it will only emit the last value in the returned observable.
– Balázs Édes
Nov 15 '18 at 9:50
What would you expect to get as result? The simulating functions you propose just emit 1 element. In the real case do you expect any of the Observables actually emit more than 1 value?
– Picci
Nov 15 '18 at 12:18
I mean the finalperform()
function. It emits all 3 elements from the 3 functions in a single observable.
– Balázs Édes
Nov 19 '18 at 10:32
@Balázs Édes I have updated my answer
– Picci
Nov 19 '18 at 21:48
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53305760%2fconcat-observables-reusing-the-previous-results%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
You can try something like
getCat()
.pipe(
switchMap(cat => petCat(cat).pipe(map(mood => ({cat, mood})))),
switchMap(({cat, mood}) => getFood(cat, mood))
)
The whole idea is to use switchMap
to switch from the source Observable to the Observable returned by the function passed to switchMap
as parameter.
Maybe worth of mentioning is the use of map
chained into the pipe of the first switchMap
. That map
operator makes sure that we keep cat
as part of the parameters we pass the second, and last, switchMap
.
UPDATE after comment
If the function perform
has to emit all the 3 elements emitted by the 3 functions, then you may try something like the following
function perform() {
return getCat()
.pipe(
switchMap(cat => petCat(cat).pipe(map(mood => ({cat, mood})))),
switchMap(({cat, mood}) => getFood(cat, mood).pipe(map(food => ({cat, mood, food}))))
)
}
Hey @Picci, thanks for the answer. The problem with this answer, is that it will only emit the last value in the returned observable.
– Balázs Édes
Nov 15 '18 at 9:50
What would you expect to get as result? The simulating functions you propose just emit 1 element. In the real case do you expect any of the Observables actually emit more than 1 value?
– Picci
Nov 15 '18 at 12:18
I mean the finalperform()
function. It emits all 3 elements from the 3 functions in a single observable.
– Balázs Édes
Nov 19 '18 at 10:32
@Balázs Édes I have updated my answer
– Picci
Nov 19 '18 at 21:48
add a comment |
You can try something like
getCat()
.pipe(
switchMap(cat => petCat(cat).pipe(map(mood => ({cat, mood})))),
switchMap(({cat, mood}) => getFood(cat, mood))
)
The whole idea is to use switchMap
to switch from the source Observable to the Observable returned by the function passed to switchMap
as parameter.
Maybe worth of mentioning is the use of map
chained into the pipe of the first switchMap
. That map
operator makes sure that we keep cat
as part of the parameters we pass the second, and last, switchMap
.
UPDATE after comment
If the function perform
has to emit all the 3 elements emitted by the 3 functions, then you may try something like the following
function perform() {
return getCat()
.pipe(
switchMap(cat => petCat(cat).pipe(map(mood => ({cat, mood})))),
switchMap(({cat, mood}) => getFood(cat, mood).pipe(map(food => ({cat, mood, food}))))
)
}
Hey @Picci, thanks for the answer. The problem with this answer, is that it will only emit the last value in the returned observable.
– Balázs Édes
Nov 15 '18 at 9:50
What would you expect to get as result? The simulating functions you propose just emit 1 element. In the real case do you expect any of the Observables actually emit more than 1 value?
– Picci
Nov 15 '18 at 12:18
I mean the finalperform()
function. It emits all 3 elements from the 3 functions in a single observable.
– Balázs Édes
Nov 19 '18 at 10:32
@Balázs Édes I have updated my answer
– Picci
Nov 19 '18 at 21:48
add a comment |
You can try something like
getCat()
.pipe(
switchMap(cat => petCat(cat).pipe(map(mood => ({cat, mood})))),
switchMap(({cat, mood}) => getFood(cat, mood))
)
The whole idea is to use switchMap
to switch from the source Observable to the Observable returned by the function passed to switchMap
as parameter.
Maybe worth of mentioning is the use of map
chained into the pipe of the first switchMap
. That map
operator makes sure that we keep cat
as part of the parameters we pass the second, and last, switchMap
.
UPDATE after comment
If the function perform
has to emit all the 3 elements emitted by the 3 functions, then you may try something like the following
function perform() {
return getCat()
.pipe(
switchMap(cat => petCat(cat).pipe(map(mood => ({cat, mood})))),
switchMap(({cat, mood}) => getFood(cat, mood).pipe(map(food => ({cat, mood, food}))))
)
}
You can try something like
getCat()
.pipe(
switchMap(cat => petCat(cat).pipe(map(mood => ({cat, mood})))),
switchMap(({cat, mood}) => getFood(cat, mood))
)
The whole idea is to use switchMap
to switch from the source Observable to the Observable returned by the function passed to switchMap
as parameter.
Maybe worth of mentioning is the use of map
chained into the pipe of the first switchMap
. That map
operator makes sure that we keep cat
as part of the parameters we pass the second, and last, switchMap
.
UPDATE after comment
If the function perform
has to emit all the 3 elements emitted by the 3 functions, then you may try something like the following
function perform() {
return getCat()
.pipe(
switchMap(cat => petCat(cat).pipe(map(mood => ({cat, mood})))),
switchMap(({cat, mood}) => getFood(cat, mood).pipe(map(food => ({cat, mood, food}))))
)
}
edited Nov 19 '18 at 21:47
answered Nov 14 '18 at 18:00
PicciPicci
5,37252749
5,37252749
Hey @Picci, thanks for the answer. The problem with this answer, is that it will only emit the last value in the returned observable.
– Balázs Édes
Nov 15 '18 at 9:50
What would you expect to get as result? The simulating functions you propose just emit 1 element. In the real case do you expect any of the Observables actually emit more than 1 value?
– Picci
Nov 15 '18 at 12:18
I mean the finalperform()
function. It emits all 3 elements from the 3 functions in a single observable.
– Balázs Édes
Nov 19 '18 at 10:32
@Balázs Édes I have updated my answer
– Picci
Nov 19 '18 at 21:48
add a comment |
Hey @Picci, thanks for the answer. The problem with this answer, is that it will only emit the last value in the returned observable.
– Balázs Édes
Nov 15 '18 at 9:50
What would you expect to get as result? The simulating functions you propose just emit 1 element. In the real case do you expect any of the Observables actually emit more than 1 value?
– Picci
Nov 15 '18 at 12:18
I mean the finalperform()
function. It emits all 3 elements from the 3 functions in a single observable.
– Balázs Édes
Nov 19 '18 at 10:32
@Balázs Édes I have updated my answer
– Picci
Nov 19 '18 at 21:48
Hey @Picci, thanks for the answer. The problem with this answer, is that it will only emit the last value in the returned observable.
– Balázs Édes
Nov 15 '18 at 9:50
Hey @Picci, thanks for the answer. The problem with this answer, is that it will only emit the last value in the returned observable.
– Balázs Édes
Nov 15 '18 at 9:50
What would you expect to get as result? The simulating functions you propose just emit 1 element. In the real case do you expect any of the Observables actually emit more than 1 value?
– Picci
Nov 15 '18 at 12:18
What would you expect to get as result? The simulating functions you propose just emit 1 element. In the real case do you expect any of the Observables actually emit more than 1 value?
– Picci
Nov 15 '18 at 12:18
I mean the final
perform()
function. It emits all 3 elements from the 3 functions in a single observable.– Balázs Édes
Nov 19 '18 at 10:32
I mean the final
perform()
function. It emits all 3 elements from the 3 functions in a single observable.– Balázs Édes
Nov 19 '18 at 10:32
@Balázs Édes I have updated my answer
– Picci
Nov 19 '18 at 21:48
@Balázs Édes I have updated my answer
– Picci
Nov 19 '18 at 21:48
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53305760%2fconcat-observables-reusing-the-previous-results%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown