RxJS retry and then catchError not working - error-handling

I have an observable:
public updateThingName(name: string, thingId: number): Observable<any> {
console.log('attempting rename');
return this.http
.put(
`${this.thingApi}/projects/${thingId}`,
{ name },
this.options,
).pipe(
map(response => response.data.id)
);
}
called as part of a longer chain:
return this.projectService.getProject(id).pipe(
switchMap(prevProjectData => {
return this.projectService.updateProject(id, data).pipe(
map(newProjectData => ({prevProjectData, newProjectData}))
)
}),
switchMap(({prevProjectData, newProjectData}) => {
return this.thingService.updateThingName(newProjectData.title, newProjectData.thingId)
.pipe(retry(5),catchError(err => {
return this.projectService.revertProjectUpdate(err, prevProjectData);
}))
}),
tap(() => { ... save to logs only when successful ... })
);
I want to try to rename something, if it fails retry 5 times, if it still fails then catch the error, revert the earlier changes and throw the final error in the revert function. The reverting and sending the error response back to the front end works fine but no matter where I put the retry(5) I only ever see the initial console.log('attempting rename'); in the logs.
Am I miss using the retry? How do I get this to work?
This is backend code on NestJS so I don't handle the final subscribe aspects directly if that makes a difference.
Thanks!

That should be correct. The methodos called once but it tries to resubscribe multiple times. You should be able to see it if you log a message on every subscribe:
public updateThingName(name: string, thingId: number): Observable<any> {
return defer(() => {
console.log('attempting rename');
return this.http
.put(
`${this.thingApi}/projects/${thingId}`,
{ name },
this.options,
).pipe(
map(response => response.data.id)
);
)};
}

Related

React Native redux-saga - How to achieve yield all when getting a response and saving a separate record

Currently I have a piece of code that request an api call and then base from the response:
OK
Error
I will have to create another pending record this is due to intermittent connection issue so i have to deal with an event that happened and store it and use it later.
const response = yield call(api.login, credentials)
yield all(
response.matchWith({
Ok: ({ value }) => {
put(Actions.pendingRecord(null))
return put(Actions.apiSuccess(value))
},
Error: ({ value }) => {
const pendingRecord = {
value: true,
data: {
event: "queue"
}
}
put(Actions.pendingRecord(pendingRecord))
return put(Actions.apiFailure(value))
}
})
)
is it possible to have multiple put actions when calling yield all ?
I'm referring to this:
put(Actions.pendingRecord(null))
return put(Actions.apiSuccess(value))
With https://www.npmjs.com/package/redux-batched-actions you can do it like that:
return put(batchActions([
Actions.pendingRecord(null),
Actions.apiSuccess(value)
]);

Is it possible to fire a .then() block without a return value from a promise?

Curious issue I'm having dealing with some callback functions. I need to make a series of API calls that all return promises then I'm trying to take that data and map it to an array that exists on the global scope followed by a function to export the new data as a pdf - my issue is that the then() block is firing before the other function finishes and far before the first API call finshes. `
let fireWatson = async () => {
let watsonClassifed = []
let watsonCallIndex = 0;
let returnedArr = []
for (let i = 0; i < watsonData.length; i++) {
let params = {
classifierId: '***********',
collection: watsonData[i]
}
naturalLanguageClassifier.classifyCollection(params,
function (err, response) {
if (err)
console.log('error:', err);
else
console.log("data returned")
console.log(response.result.collection)
watsonClassifed.push(response.result.collection);
console.log(watsonClassifed)
})
}
}
fireWatson(watsonData).then(res =>
console.log("firing stupid callbback after data")
)
I realize this function isnt actually returning anything but is it possible to still make use of a promise without a return value or is this the main issue im hitting? Ideally - i want the then function to wait until the data is back - mapped to the global array and then outputted but this of course depends on proper synchronicity.
output:
[Done] exited with code=0 in 1.526 seconds
[Running] node "index.js"
firing stupid callbback
data returned
all my sweet sweet data
All functions in JavaScript have returns, it's just that they are implicit if you don't say return explicitly
It's always a bit tricky to mix promises with callbacks. Here is a way you can fireWatson without using any utilities.
let fireWatson = async watsonData => Promise.all(watsonData.map(collection => new Promise((resolve, reject) => {
let params = {
classifierId: '***********',
collection: collection,
}
return naturalLanguageClassifier.classifyCollection(params, function(err, response) => {
if (err) {
reject(err)
} else {
resolve(response)
}
})
})))
Of course, you can simplify this tremendously using a utility I created
const { map } = require('rubico')
let fireWatson = map(collection => new Promise((resolve, reject) => {
let params = {
classifierId: '***********',
collection: watsonData[i]
}
return naturalLanguageClassifier.classifyCollection(params, function(err, response) => {
if (err) {
reject(err)
} else {
resolve(response)
}
})
}))
turns out console.log was firing because every .then() block expects a function.
wrong:
fireWatson(watsonData).then(res =>
console.log("firing stupid callbback after data")
)
right:
fireWatson(watsonData).then(()res =>
console.log("firing stupid callbback after data")
)

Perform patch operation on the data

Suppose I have
student: {
id: 'asdj2334',
name: 'asdcjn',
}
How do I send patch request/method after updating name of the object. suppose axios.patch()
Update1: I did the same as said below and I am getting success but it is not being updated on serve.
patchMedication (medication) {
/* eslint-disable */
console.log(medication)
return API.patch('/medicationRegisters/' + medication.id, {medication})
.then(response => {
return response.data
})
},
on console I get :
And after success it is not updated
You could use put request in your axios. see code below
axios.put('api endpoint'+this.student.id, this.student).then(({data}) => {
... success
})

Error handling in Vue JS - errors not caught

I have the following code:
fooService.update(this.bar).then( this.$emit('updated', this.updatedBar),).catch(err => {...
If an error is encountered, then the error is not caught. If I change the code to be:
fooService.update(this.bar).then(x => {this.$emit('updated', this.updatedBar);}).catch(err => {...
Then the error is caught and shows as expected. Can anyone explain to me what is going on and why it behaves in that way?
Edit
Underlying service code:
function updateBar(bar) {
return $http.put(`/api/bar/${bar.Id}`, bar);
}
So I still think the error is happening in the this.$emit the reason why, in
fooService.update(this.bar).then( this.$emit('updated', this.updatedBar),).catch(err => {
It has to evaluate the this.$emit first as you're setting the response from that function as the .then and not the call itself.
Proof of it doing that
function emit(){
console.log('emit')
}
var promise = new Promise(function(resolve,reject){
setTimeout(() => {
console.log('promise is done')
reject();
}, 1000)
})
promise.then(emit()).catch( function() {console.log('carry on');})
notice how it logs "emit" first
Now if that errors you can see it doesn't hit the catch
function emit(){
console.log('emit')
throw new Error("bad")
}
var promise = new Promise(function(resolve,reject){
setTimeout(() => {
console.log('promise is done')
reject();
}, 1000)
})
promise.then(emit()).catch( function() {console.log('carry on');})
So under the hood it's doing this (the simplest way I can think of)
emit()
try{
getService()
} catch{
...
}
Whereas if you actually pass the .then a function it changes the order of things
function emit(){
console.log('emit')
throw new Error("bad")
}
var promise = new Promise(function(resolve,reject){
setTimeout(() => {
console.log('promise is done')
reject();
}, 1000)
})
promise.then(() => {emit()}).catch( function() {console.log('carry on');})
and again under the hood it looks like this
try{
getService()
emit()
} catch{
...
}

HapiJS reply being called twice

I'm getting this error message with the snippet below
Unhandled rejection Error: reply interface called twice
Note that I'm using return for all the reply() interface
Locations
.findOne({
_id: request.params.id,
activationCode: payload.activationCode
}).then((location) => {
if (!location) {
return reply(Boom.notFound('Location not found'))
}
locationObject = location
if (payload.password !== payload.confirmPassword) {
return reply(Boom.badRequest('Password and Confirm Password must match'))
}
if (!payload.terms) {
return reply(Boom.badRequest('Must agree to Terms & Conditions'))
}
return newPaymentMethod.save()
}).then((paymentMethod) => {
.....
return user.save() // user is defined at .....
}).then(() => {
return reply({ token: helpers.createJwt(userObject) })
}).catch((err) => {
return reply(Boom.wrap(err))
})
Any help would be appreciated.
Looks like you get caught into this due to the incorrect use of promises. I guess you’re executing your snippet within a route handler where you’ve access to reply.
As you’re returning your responses within the promise chain, you both return the value to the next .then (promise) and also calling the reply from the outer scope.
I suggest you use a promise reject for errors so that you only need a single reply(Boom.method()) within the promise’s .catch().
Since you chain promises in the end
.then(() => {
return reply({ token: helpers.createJwt(userObject) })
}).catch((err) => {
return reply(Boom.wrap(err))
})
you might call reply twice if any of if conditions is true.
Easy solution would be to throw error in if condition is true - since there is already a Boom.wrap in catch block.