I have the next Observable i trying to filter to get the user in network but .mapTo(phone => verifyPhoneInNetwork(phone, country)) return AjaxObservable instead of the ajax response
function verifyInNetwork(contacts: any, country: string) {
const inNetworkOb = Observable
.from(contacts)
.map(contact => contact.phones)
.map(phone => verifyPhoneInNetwork(phone, country))
.first(({response}) => {
return !response.invalid && !response.exists;
})
.isEmpty()
.filter(empty => empty);
If verifyPhoneInNetowrk returns an Observable you should use switchMap like so:
function verifyInNetwork(contacts: any, country: string) {
const inNetworkOb = Observable
.from(contacts)
.map(contact => contact.phones)
.switchMap(phone => verifyPhoneInNetwork(phone, country))
.first(({response}) => {
return !response.invalid && !response.exists;
})
.isEmpty()
.filter(empty => empty);
Learn more about switchMap.
Related
Demo: https://stackblitz.com/edit/rxjs-unsubscribe-issue?file=index.ts
Below code is not working
Error: Cannot read property 'unsubscribe' of undefined
const a = (): Observable<any> =>
new Observable(sub => {
sub.next(1);
return () => {
console.log('unsubscribe');
};
});
const observer = a().subscribe(
value => {
console.log('Subscription');
observer.unsubscribe();
},
e => console.log(e),
() => console.log('complete')
);
But the following code is working
const b = (): Observable<any> =>
new Observable(sub => {
setTimeout(()=>sub.next(1),0);
return () => {
console.log('unsubscribe');
};
});
const observer2 = b().subscribe(
value => {
console.log('Subscription b');
observer2.unsubscribe();
},
e => console.log(e),
() => console.log('complete')
);
Help me understand the reason behind it
as you mentioned in the title of your question, the first example is synchronous, so you get the first value while still inside of the .subscribe() method. Naturally, observer, which is supposed to have a Subscription object hasn't been initialized yet.
If you want to unsubscribe after receiving a single value I would suggest to use .take(1)
How can I make sure the tap operator is called even if a subscription is unsubscribed? Imagine the following:
function doUpdate() {
return this.http.post('somewhere', {})
.pipe(
tap(res => console.log(res))
)
}
const sub = doUpdate().subscribe(res => /* do something with res */);
setTimeout(() => sub.unsubscribe(), 1000)
In this case, I just want to prevent the subscribe action from being executed, yet I want to make sure the console log is fired, even if the request took longer than 1000 milliseconds to execute (and in this particular case, I don't even want the POST to be cancelled either).
use finalize() operator, although that will also get called when observable is completed
function doUpdate() {
return this.http.post('somewhere', {})
.pipe(
finalize(() => console.log(res))
)
}
some code to demonstrate the idea:
https://stackblitz.com/edit/rxjs-playground-test-m9ujv9
In Rxjs 7.4 tap now has three more subscribe handlers, so you can use it to get notified on subscribe, unsubscribe and finalize:
https://github.com/ReactiveX/rxjs/commit/eb26cbc4488c9953cdde565b598b1dbdeeeee9ea#diff-93cd3ac7329d72ed4ded62c6cbae17b6bdceb643fa7c1faa6f389729773364cc
So you can do:
const subscription = subject
.pipe(
tap({
subscribe: () => results.push('subscribe'),
next: (value) => results.push(`next ${value}`),
error: (err) => results.push(`error: ${err.message}`),
complete: () => results.push('complete'),
unsubscribe: () => results.push('unsubscribe'),
finalize: () => results.push('finalize'),
})
)
.subscribe();
Unsubscribing from a http request will definitely cancel the request, you could try shareReplay
function doUpdate() {
return this.http.post('somewhere', {})
.pipe(
tap(res => console.log(res)),
shareReplay()
)
}
and if that doesn't work the passing the result into a subject
function doUpdate() {
const subject = new Subject();
this.http.post('somewhere', {})
.pipe(
tap(res => console.log(res))
).subscribe(subject);
return subject;
}
I am trying to combine fetch API and promises
When I do this, everything works
queryAPI(currency, cryptocurrency){
const url = fetch('https://api.coinmarketcap.com/v1/ticker/')
.then(response => response.json())
.then(data => console.log(data));
}
However, when I try to store it in a variable, the promise keeps pending
queryAPI(currency, cryptocurrency){
const url = fetch('https://api.coinmarketcap.com/v1/ticker/')
.then(response => {
const user = response.json()
console.log(user);
});
}
1) What am I doing wrong?
2) Is there any way I can get the value of the "user" outside of the function?
Thanks
The .json method also returns a promise. You have to call .then once again to get the final result:
queryAPI(currency, cryptocurrency){
const url = fetch('https://api.coinmarketcap.com/v1/ticker/')
.then(response => response.json())
.then(user => console.log(user))
}
so you could return the chained .thens from the queryAPI method and use it in the rest of your code:
const queryAPI = (currency, cryptocurrency) => {
return new Promise((resolve, rej) => {
return fetch('https://api.coinmarketcap.com/v1/ticker/')
.then(response => response.json())
.then(data => resolve({user: data, currency: 'EUR'}))
.catch(e => rej(e))
});
};
queryAPI('EU', 'bitcoin').then(data => {
console.log(data.user);
console.log(data.currency)
});
getComments is async function. When it returns an error, I want to handle it through catchError. But I always execute map() and never execute catchError.
Why and How to fix this?
from(getComments(action.payload.url)).pipe(
map((comments: IComments[]) => commentsActions.fetch.done({ params: action.payload.url, result: { comments } })),
// TODO: 以下ではError handlingができない
catchError(error => of(commentsActions.fetch.failed({ params: action.payload.url, error: { hasError: true } }))),
),
and
export const getComments = async (url: string) => {
return await fetch(url)
.then(response => response.json())
.then(comments => comments)
.catch((e: string) => throwError(e));
}
Thanks in advance.
I handle error with ajax and it works.
https://github.com/redux-observable/redux-observable/blob/master/docs/recipes/ErrorHandling.md
I am trying to get my NgRX effect to return an action object. The action object indicates a "success", and needs to return all retrieved institutions as part of the payload. How do I do this? Please note that the block inside of the mergeMap does not work. I need to return all the retrieved institutions as part of a payload after retrieving them.
#Effect()
getInstitutions$ = this.actions$
.ofType(institutionActions.InstitutionActionTypes.GetInstitutions)
.pipe(
switchMap(() => {
const institutions = this.getInstitutions();
console.log(institutions);
return this.getInstitutions();
}),
mergeMap((institutions: Institution[]) => {
// return institutions;
return {
type: institutionActions.InstitutionActionTypes.GetInstitutionsSuccess,
payload: institutions
};
})
);
You have to operate on this.getInstitutions() as in:
#Effect()
getInstitutions$ = this.actions$
.ofType(institutionActions.InstitutionActionTypes.GetInstitutions)
.pipe(
switchMap(() => {
return this.getInstitutions().pipe(
map(institutions => ({
type: institutionActions.InstitutionActionTypes.GetInstitutionsSuccess,
payload: institutions
})),
catchError(_ => ...) //don't forget to handle errors
)
})
);
As a side note, I would encourage you to use action creators, instead of creating action objects "all over the place" - Let’s have a chat about Actions and Action Creators within NgRx
How about using "map" instaed mergeMap. Can you try following?
#Effect()
getInstitutions$ = this.actions$
.ofType(institutionActions.InstitutionActionTypes.GetInstitutions)
.pipe(
switchMap(() => {
const institutions = this.getInstitutions();
console.log(institutions);
return this.getInstitutions();
}),
map((institutions: Institution[]) => {
// return institutions;
return {
type: institutionActions.InstitutionActionTypes.GetInstitutionsSuccess,
payload: institutions
};
})
);
Hope that helps!