Get event from inner observable - http

public testFunction(name: string, model: string): Observable<any> {
return this.doSomething(name, model)
.flatMap((result: any) => {
return this.doSomethingElse()... // returning an Observable<any>
});
}
That doSomething is a HTTP call, actually returning an Observable by itself.
Here is were i call that method:
public foo () {
this.testFunction(name, model)
.subscribe(
(result) => {
// do something
},
(error) => {
// do something wit the error
});
}
Problem is: how can i catch if the doSomething HTTP call went good or bad from within foo?
I do not want to use a Subject or Behaviour subject since testFunction is part of a service and foo is on a component. I mean, don't want to add a "superstructure" to achieve it.

You could check my solution on:
Notify from inner flatMap
Quite simple and you don't have to merge different observables.

Found a solution: by splitting that big observable i can handle two differents onNext instead of just one.
This allows me to know when the first method (doSomething) ends.
Here a plunkr: https://plnkr.co/edit/crWpj8deQzePCrTtYz6k?p=preview
public testFunction(name: string, model: string): Observable<any> {
let obs1 = this.doSomething(name, model);
let obs2 = obs1.flatMap((data) => {
return this.doSomethingElse() //[...]
});
let merge = Observable.merge(obs1, obs2);
return merge
}
public foo () {
this.testFunction(name, model)
.subscribe(
(result) => {
// 1 - Obs1 next
// 2 - Obs2 next
},
(error) => {
// do something wit the error
});
}

Your current code does exactly what you want. If an onError is raised in DoSomethingElse it is emitted to the main stream and will end up in your subscription onError in the foo() method.
If you want to have more control over how your succes/error is propagated you could also do something like this:
public testFunction(name: string, model: string): Observable<any> {
return this.doSomething(name, model)
.flatMap((result: any) => this.doSomethingElse()
.map((doSomethingElseResult:any) => ({ result:'succes', data: doSomethingElseResult })
.catch(err => Rx.Observable.just({ result: 'do-something-else-error', error: err }))
)
.catch(err => Rx.Observable.just({ result: 'do-something-error', error:err });
}
This will convert all errors to onNext values which have a type for you to handle upon if the type is do-something-else-error

Related

Confused with this async thunk

export const loginSuccess = createAsyncThunk(
"auth/loginSuccess",
async (user: User) => {
const res = await api
.post(
"/auth/loginSuccess",
{ user },
{
withCredentials: true,
}
)
.then((res: any) => {
setAxiosToken(res.data.token);
saveToken(res.data.token);
return { ...res.data.data, token: res.data.token };
});
return res;
}
);
There are 2 return statements at the end so I am confused about which return value the fulfilled reducer will get. The code is written by someone else that's why I want to understand it.
The second return statement is the one which will return from your function.
The first is actually returning from the then function of the promise that axios returns.
This is made a little bit confusing by using the same name for the res variable in the thunk function, and for the response variable that is passed on the the then function.
But what you will receive back is the object generated in this line of code:
{ ...res.data.data, token: res.data.token }
Where res.data.data is spread into a new object, and res.data.token is assigned to the token property of that object.

Jest Calling async function

I have a function in redux store.
Func1(){
Dispatch(some action)
Dispatch(some action1)
result = Dispatch(some async action)\\promise retured
result.then(()=>
Dispatch(some other action)
)
}
My test case
Test(()=>{
Expectedactions= all 3 actions.
Axios.post.mockimplimetation(()=>
Promise.resolve(data))
calling Func1() from wrapper.
Expect(store.getActions()).toequal(expectedactions)
})
Test result shows only 2 actions.
Async action is not captured.
Please help me how to test this behaviour or what approach I shoud take.
Edited.
TestConrainer.js
const mapstatetoprops= (state)=>{
return {}
}
const mergeprops =(stateprops,dispatchprops,ownprops)=>{
const {dispatch}= dispatchprops
const f1=()=>{
dispatch(configActions.pause("Pause"))
let result = dispatch(DataActions.save(stateProps.data)
dispatch(configActions.setClick(false));
result.then((result) => {
dispatch(configActions.show(false));
dispatch(DataActions.StatusValue(""))
})
return Object.assign({},stateprops,ownprops,{
f1:f1
})
}
export default connect(mapstatetoprops,null,mergeprops)(Test)
DataActions.js
export const save = (data) => {
return (dispatch,getState) =>{
return axios.post(url, payload))
.then(data => {
return data;
},error => {
return error
}).catch(err){
return err
}
My test case
Test(()=>{
Expectedactions= all 3 actions.
Axios.post.mockimplimetation(()=>
Promise.resolve(data))
calling f1() from wrapper like
Wrapper.shallow(<TestContainer store=store/>)
Wrapper.find('Test').props().f1()
Expect(store.getActions()).toequal(expectedactions)
})
Test result shows only 2 actions.
Async action is not captured.
Please help me how to test this behaviour or what approach I shoud take.

how to return from async to normal execution in typescript

in TypeScript, to resolve the promise, I use the await keyword.
but that keyword is only allowed to exist in the body of async functions, which return Promise<T>.
in that case, the function that calls this async function, will need to resolve the return value: Promise<T>, which means i'll again need the await keyword, and that function will have to be defined as async as well.
What am I missing?
You don't have to await, you can use then on the promise.
Here's the scenario you've described:
async function fn(): Promise<number> {
return new Promise<number>((resolve, reject) => {
setTimeout(resolve.bind(null, 2), 1500);
});
}
async function fn2(num: number) {
return await fn() * num;
}
function fn3(num: number) {
fn2(num).then(num => {
console.log(`returned number: ${num}`);
})
}
fn3(10);
(code in playground)
Which outputs this after 1500 milliseconds:
returned number: 20
Edit
Doing this:
var x = fn2(3).then(num => { return num * 2 });
console.log(x);
Will log something like:
Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}
To get the value you need to use then:
x.then(num => console.log(num));
You can not assign the result of an async operation into a variable without using await.

Angular2 Observable HTTP Data not filled

I´m fairly new to Angular2 and want to load data from a JSON-file.
I read the AngularIO Article and did the tutorial, but i still need some help.
I splitted the "getting Data" into an apiService which loads data and a component, whicht gets the data from the apiService.
Because it´s simpler, for this question I want to load the data directly to the component without the apiService.
This is the component:
export class StatusComponent implements OnInit {
private status = {};
private error: string;
constructor(private router: Router, private variables: Variables, private apiService: ApiService, private http: Http) {
if (!this.variables.getIsLoggedIn()) {
this.router.navigate(['login']);
}
}
ngOnInit() {
this.getStatus();
}
getStatus() {
this.http.get('../app/apiFiles/status.json')
.map((res:Response) => res.json())
.subscribe(
data => { this.status = data},
err => console.error(err),
() => console.log('done')
);
console.log(JSON.stringify(this.status));
}
}
This is JSON:
{
"status": {
"timercount": "0",
"reccount": "0"
}
}
In the component, the getStatus() is correctly load, and goes through without an error, but the variable i fill status isn´t filled.
After the getStatus() the variable is still {}.
Moreover, the output of console.log(JSON.stringify(this.status)); is {}.
I hope someone can help me and solve it !
Thank so much!
That's because console.log(JSON.stringify(this.status)); is executed after http call is done. Have in mind that all http calls are asynchronous methods. Try this:
getStatus() {
this.http.get('../app/apiFiles/status.json')
.map((res:Response) => res.json())
.subscribe(
data => { this.status = data},
err => console.error(err),
() => console.log(this.status);
);
}
You'll see this will print your result because () => console.log(this.status); is executed after http call is successfuly completed.
The correct solution is this:
getStatus() {
this.http.get('../app/apiFiles/status.json')
.map((res:Response) => res.json())
.subscribe(
data => { this.status = data.status},
err => console.error(err),
() => console.log('done')
);
console.log(JSON.stringify(this.status));
}
So, you have to use data.status, because the JSON file begins with "status" ;)

RxJS and AngularJS HTTP - how can I achieve this?

I'm writing a small utility function that wrap a call to AngularJS http.get with the necessary authentication headers:
get(endpoint: string): Observable {
var headers = new Headers();
this._appendAuthentificationHeaders( headers, this.user.credentials);
return this.http.get(endpoint, { headers: headers })
.map(res => res.json());
}
The point here is that if this.user is null, the method will just crash.
So I have three options:
Return null and check that return value on every call...
Throw an exception
Find a way to also return an RxJS Observable object that will directly trigger the error handler.
I would like to implement the third method, as it would allow me unify this method's behavior: It always returns an observable no matter what happen.
Do you have an idea about how to do that?
Do I have to create a new Observable and kind of merge those two?
What can I do?
If the user is null, you can simply return a raw observable that triggers an error:
if (this.user == null) {
return Observable.create((observer) => {
observer.error('User is null');
});
}
(...)
or leverage the throw operator:
if (this.user == null) {
return Observable.throw('User is null');
}
(...)
This way the second method of the subscribe method will be called:
observable.subscribe(
(data) => {
(...)
},
(err) => {
// Will be called in this case
}
);
I think the cleanest way would be to wrap the whole function body to an observable, as it will turn any accidental error to an observable error. Something like this:
get(endpoint: string): Observable {
return Rx.Observable.defer(() => {
var headers = new Headers();
this._appendAuthentificationHeaders(headers, this.user.credentials);
return Rx.Observable.just(headers);
})
.flatMap(headers => this.http.get(endpoint, { headers: headers }))
.map(res => res.json());
}
However I still do not agree with http.get returning an observable instead of a promise. As these are single valued observables, your function could be a simple async function (sry, js instead of ts):
async get(endpoint) {
var headers = new Headers();
this._appendAuthentificationHeaders(headers, this.user.credentials);
const res = await this.http.get(endpoint, { headers })).toPromise();
return res.json();
}

Resources