Promise rejection not getting catched - javascript

Why is a promise rejection not catched here? I tried to debug it but reason never gets executed
axios.post('http://localhost:5000/', {
contractId: contractValue
}).then(() => {
setLoading(false)
setButton(null)
}, (reason) => {
console.log(reason)
})

Instead try putting a catch in your promise chain. It will catch any exceptions in the chain before it. It would look something like this:
axios.post('http://localhost:5000/', {
contractId: contractValue,
})
.then(() => {
setLoading(false)
setButton(null)
})
.catch(err => {
// this is where you handle your exception
console.log(err)
})

Instead of passing the function as a second argument, you can add the .catch block that will handle all exceptions when thrown
axios.post('http://localhost:5000/', {
contractId: contractValue
}).then(() => {
setLoading(false)
setButton(null)
}).catch((reason) => {
console.log(reason)
})

onRejected never handles rejected promises from the same .then(onFulfilled) callback and .catch takes both.
const getPromise = () => new Promise((resolve, reject) => { Math.round(Math.random()) ? resolve('resolve #1') : reject('reject #1')})
getPromise().then(result => { throw new Error('reject #2')}, error => { // Handles only 'reject #1'})
getPromise().then(result => { throw new Error('reject #2')}) .catch(error => { // Handles both 'reject #1', // and 'reject #2' }))
Above description was from this link: The real difference between catch vs onRejected

Related

Promise Reject Callback is not called but Resolve Callback is being called as expected

So I have a problem regarding Promises currently and I'm not sure why it is not working as expected.
In my Angular Library I have the following code:
public async load() {
return new Promise<void>((resolve, reject) => {
this.service.isReady$
.pipe(
take(2),
filter((result) => result.ready),
switchMap(() => {
return this.loadClassifierFiles();
}),
tap(() => {
console.log('Classifier Files should load now');
})
)
.subscribe({
next: () => {
try {
// do stuff here ...
resolve();
} catch {
reject(new Error('Could not load all Classifier Files'));
}
},
error: () => {
reject(
new Error('Could not fetch all Classifier Files from Destination')
);
},
});
});
}
In my Application I have the following code:
public async changeSomething() {
await this.service
.changeStuff()
.then(() => {
this.modelStatus = ModelChangeStatus.SUCCESS;
})
.catch((error) => {
this.modelStatus = ModelChangeStatus.FAILURE;
});
}
In the test of my Library I have verified that for example on an error when loading the files during this.loadClassifierFiles() I will get into the error block of my subscription and therefore the Promise should be rejected. However, only when I resolve the Promise the callback in my Application is called. When I reject the Promise only the error handling of the library is being called but not the catch block in my application that I need for a status update.
I also tried the following but nothing changed:
this.openCvService
.changeStuff(tempConfig)
.then(
() => {
this.modelStatus = ModelChangeStatus.SUCCESS;
},
(error: Error) => {
console.log(error.message);
this.modelStatus = ModelChangeStatus.FAILURE;
}
)
Any ideas why the rejection callback is not being called?

javascript promise handling, fail to handle error

I'm having some trouble understanding what I'm doing wrong. I have a function that receives a url to which should make a GET request, in case of success should fill a combo with the received data (this depends which function calls it), in case of fail it should execute some common code.
getFirstCombo = () => {
this.getFromApi('/First/GetAll')
.then(data => this.setState({firstComboOptions: this.parseCombo(data)}))
.catch(error => console.log('ERROR2: ', error));
}
getSecondCombo = () => {
this.getFromApi('/Second/GetAll')
.then(data => this.setState({secondComboOptions: this.parseCombo(data)}))
.catch(error => console.log('ERROR2: ', error));
}
parseCombo = (data: any) => {
const combo = data.map(item => (
{ label: item.description, value: item.id }
));
return combo;
}
getFromApi = (url: string) : Promise<any> => {
return restApiAxios.get(url)
.then(response => {
return response.data;
})
.catch(error => {
console.log('ERROR: ', error);
});
}
this code is executed on the componentDidMount of the react component, but when it fails, it first prints :
ERROR: Error: Network Error
at createError (createError.js:16)
at XMLHttpRequest.handleError (xhr.js:83)
and immediately after:
PanelDatos.tsx:50 ERROR2: TypeError: Cannot read property 'map' of undefined
at PanelDatos.parseCombo (PanelDatos.tsx:55)
at PanelDatos.tsx:50
so, when failing executes the catch block from getFromApi and then it tries to execute the then block in getFirstCombo, which triggers the catch block from the same function cause data does not exist, why is that? shouldnt it just execute the first catch?
thanks in advance
.catch returns a promise much like .then, allowing you to return a custom value and handle it that way.
Try doing the following to observe the effect:
Promise
.reject(1)
.catch(e => e) // Catch the error and return it
.then(console.log) // will log 1 to the console
This means you'll need to add some checks if you want to continue to use promises like this:
Promise
.reject(new Error('haha'))
.catch(err => ({err}))
.then(({err, data}) => {
if(err) return // Do nothing
// enter code here
})
However, using async / await will improve readability even more:
getFirstCombo = async () => {
let response
try {
response = await this.getFromApi('/First/GetAll')
} catch (e) {
return // Exit early
}
let parsed
try {
parsed = this.parseCombo(data)
} catch (e) {
console.log(e)
return // Exit early
}
return this.setState({firstComboOptions: parsed})
}
And, of course, throw the error again in your catch block in your api to allow it to handle api calls.
This is happening since inside getFromApi catch method on the error you are not returning anything, so by default, it is returning a resolved promise with null response and the execution goes inside getFirstCombo then method, causing another error. You can update your code to resolve this like:
getFromApi = (url: string): Promise<any> => {
return restApiAxios.get(url)
.then(response => response.data)
.catch(error => Promise.reject(error));
}
The static Promise.reject function returns a Promise that is rejected. So, it will go directly into catch of wherever getFromApi is called.
DEMO:
async function getFromApi(url) {
return fetch(url) // rejects
.then(response => response.json())
.catch(err => Promise.reject(err))
}
async function getFirstCombo() {
getFromApi('https://no-such-server.abcd')
.then(data => console.log('data: ', data))
.catch(error => console.log('ERROR2: ', error));
}
getFirstCombo()
DEMO #2 (With getFirstCombo function not having any catch block) :
async function getFromApi(url) {
return fetch(url) // rejects
.then(response => response.json())
.catch(err => {
console.log('ERROR in getFromApi(): ', err);
return null; // return null, empty array, 0 or false... as per your requirement
})
}
async function getFirstCombo() {
getFromApi('https://no-such-server.abcd')
.then(data => console.log('data: ', data))
// Same value set in catch block of getFromApi will return in this then() block
// Validate this `data` variable before processing it further like:
// if(data === null) this means an error had occurred
// else continue with your logic
}
getFirstCombo()

Promise.reject in .then() returning undefined

I've currently got an ES6 class with a constructor and two methods. I'm a tad confused as to why using Promise.reject(ex) within the .then() is resolving undefined. If someone wouldn't mind explaining what I'm doing wrong that would be much appreciated.
I have a method called getYaml() which contains the following:
_getYaml(recordId) {
return new Promise((resolve, reject) => {
fs.readFile(this.workingDir + '/' + recordId + '.yaml', 'utf8', function(err, data) {
if (err) reject(err)
resolve(data)
})
})
}
I then have another method called getCompDoc which makes use of the other method like so:
getCompDoc(recordId) {
return this._getYaml(recordId).then(data => {
let yaml = data
let yamlObj
try {
yamlObj = YAML.safeLoad(yaml)
} catch (ex) {
ex.message = `Failure to parse yaml. Error: ${ex.message}`
logger.error(ex.message, {}, ex)
return Promise.reject(ex)
}
let compDoc = {
// ...
}
return compDoc
}).catch(err => {
logger.error(err, {}, err)
})
}
I then have a test to check that the YAML parsing error is caught and then a promise rejected which looks like so:
describe('error cases', () => {
const fakeRecordId = 'SomeYaml'
beforeEach(() => {
sinon.stub(myClass, '_getYaml').returns(Promise.resolve('{{&^%}egrinv&alidgj%^%^&$£#£#£}'))
})
afterEach(() => {
myClass._getYaml.restore()
})
it('Error parsing yaml, rejects with error', () => {
return expect(myClass.getCompDoc(fakeRecordId)).to.be.rejected.then(response => {
expect(response.message).to.match(/Failure to parse yaml. Error: /)
})
})
})
Test output:
AssertionError: expected promise to be rejected but it was fulfilled with undefined
If I simply return the exception that is thrown within the getCompDoc method, I recieve the error as expected, however as soon as I use Promise.reject it resolves with undefined.
I was thinking of wrapping the getCompDoc in a return new Promise() however I wasn't sure if this would be an example of the Promise constructor anti-pattern. I would ideally like to reject this, instead of returning the error directly as then I can assert that the method was rejected and not fulfilled.
You 'swallow' the error in getCompDoc in your catch clause. Specifically, here's a simplified snippet representing your code:
let getYamlPromise = Promise.reject('REJECTED!');
let getCompDocPromise = getYamlPromise
.then(data => console.log('getYamlPromise', data))
.catch(error => console.error('getYamlPromise', error));
getCompDocPromise
.then(a => console.log('getCompDocPromise RESOLVED', a))
.catch(a => console.log('getCompDocPromise REJECTED', a));
As you can see, getCompDocPromise is resolved with undefined. If you would like to propagate the error, your catch clause will have to throw a new error or return a rejected promise:
let getYamlPromise = Promise.reject('REJECTED!');
let getCompDocPromise = getYamlPromise
.then(data => console.log('getYamlPromise', data))
.catch(error => {
console.error('getYamlPromise', error);
return Promise.reject(error);
});
getCompDocPromise
.then(a => console.log('getCompDocPromise RESOLVED', a))
.catch(a => console.log('getCompDocPromise REJECTED', a));

Javascript Catch In Then Promises

I am trying to figure this out. I have a promise like this
function Function1 () {
return fetch()
.then((xx) => )
.catch(error => throw(error));
}
Use this Function1 promise in another file.
Function1()
.then((xx) => ()
.catch((error) => {
console.log('I want to Catch that stupid error here');
});
Why can't I get the error message thrown from the Function1 promise in the catch error where I am calling this Function1() ?
Any of your kind help and comments will be highly appreciated, Gracious :)
Use throw inside .then function.
// Here is Promise then throw example
new Promise((resolve, reject) => {
resolve(5);
}).then(result => {
throw 'Err';
})
.catch(error => {
console.log(error);
throw error;
});

TypeScript error TS2345 when rejecting a Promise with an error

I have a TypeScript error message whose error I do not understand. The error message is:
error TS2345: Argument of type '(error: Error) => void | Promise' is not assignable to parameter of type '(reason: any) => IdentityKeyPair | PromiseLike'.
Type 'void | Promise' is not assignable to type 'IdentityKeyPair | PromiseLike'.
My code was working fine but TypeScript got mad at me when I changed this block:
.catch((error) => {
let identity: Proteus.keys.IdentityKeyPair = Proteus.keys.IdentityKeyPair.new();
return this.store.save_identity(identity);
})
into this:
.catch((error) => {
if (error instanceof RecordNotFoundError) {
let identity: Proteus.keys.IdentityKeyPair = Proteus.keys.IdentityKeyPair.new();
return this.store.save_identity(identity);
} else {
return reject(error);
}
})
Here is the complete code which was working:
public init(): Promise<Array<Proteus.keys.PreKey>> {
return new Promise((resolve, reject) => {
this.store.load_identity()
.catch((error) => {
let identity: Proteus.keys.IdentityKeyPair = Proteus.keys.IdentityKeyPair.new();
return this.store.save_identity(identity);
})
.then((identity: Proteus.keys.IdentityKeyPair) => {
this.identity = identity;
return this.store.load_prekey(Proteus.keys.PreKey.MAX_PREKEY_ID);
})
.then((lastResortPreKey: Proteus.keys.PreKey) => {
return resolve(lastResortPreKey);
})
.catch(reject);
});
}
And here is the code which does not compile anymore:
public init(): Promise<Array<Proteus.keys.PreKey>> {
return new Promise((resolve, reject) => {
this.store.load_identity()
.catch((error) => {
if (error instanceof RecordNotFoundError) {
let identity: Proteus.keys.IdentityKeyPair = Proteus.keys.IdentityKeyPair.new();
return this.store.save_identity(identity);
} else {
return reject(error);
}
})
.then((identity: Proteus.keys.IdentityKeyPair) => {
this.identity = identity;
return this.store.load_prekey(Proteus.keys.PreKey.MAX_PREKEY_ID);
})
.then((lastResortPreKey: Proteus.keys.PreKey) => {
return resolve(lastResortPreKey);
})
.catch(reject);
});
}
Does anyone sees why the TypeScript compiler refuses my return reject(error); statement with error code TS2345?
Screenshot:
I am using TypeScript 2.1.4.
Try out below. When you are in a then or catch block you can return a Promise or a value which gets wrapped into a Promise. You are manually working with a Promise yourself so you can just call the resolve and reject handlers without needing to return anything. Returning reject(error) would try to take that returned value, wrap it in a Promise and then try to pass to the next then block which is why you were getting the error you did. Think of it this way: returning something in a handler means continue down the chain with this new value. In your case I think you just want to stop the chaining and have the Promise you are creating resolve or reject under certain conditions.
public init(): Promise<Array<Proteus.keys.PreKey>> {
return new Promise((resolve, reject) => {
this.store.load_identity()
.catch((error) => {
if (error instanceof RecordNotFoundError) {
let identity: Proteus.keys.IdentityKeyPair = Proteus.keys.IdentityKeyPair.new();
return this.store.save_identity(identity);
} else {
throw error;
}
})
.then((identity: Proteus.keys.IdentityKeyPair) => {
this.identity = identity;
return this.store.load_prekey(Proteus.keys.PreKey.MAX_PREKEY_ID);
})
.then((lastResortPreKey: Proteus.keys.PreKey) => {
resolve(lastResortPreKey);
})
.catch((error) => {
reject(error);
});
});
}
You can't stop a Promise chain (cancellation aside), not even by returnning reject(), which is a definite misuse of Promises (you're not supposed to wrap a Promise in another Promise constructor).
Let's start with what you could do, then go to what you should do.
You could let the rejection bubble down the Promise chain, rethrowing it when it doesn't match your type guard, and at the bottom of the line, after all the .catch() clauses exhausted themselves, the Promise returned from your function will reject.
Now
Think about how you would do it in sync code. You'd have something like this:
try {
try {
actionThatThrows();
} catch (err) {
breakEverything();
}
continue other steps
} catch(err) {
generalErrorHandling();
}
That kind of code is not OK, and it isn't OK in Promises either. You should move distinct actions into functions which can resolve or reject on their own, use Errors as they were meant, an exception that bubbles up the stack until it meets something that can handle it.
Also, and because you're using TS 2.1.x, for long async flows, an async function is recommended.
Your return is useless there, it's the end of the onRejection callback.
And the return reject() will fulfill the next .then() promise anyway.
However, if you throw the error, it'll be inherited in the following promises down to the .catch(reject);
Basically: in any catch/then, return will resolve the child promise, and throw will reject the child promise.
I rewrote your code for a better flow of the promise chain.
public init(): Promise<Array<Proteus.keys.PreKey>> {
return new Promise((resolve, reject) => {
this.store.load_identity()
.catch(
(error) => {
if (error instanceof RecordNotFoundError) {
let identity: Proteus.keys.IdentityKeyPair = Proteus.keys.IdentityKeyPair.new();
return this.store.save_identity(identity);
} else {
throw error;
}
}
)
.then(
(identity: Proteus.keys.IdentityKeyPair) => {
this.identity = identity;
resolve(this.store.load_prekey(Proteus.keys.PreKey.MAX_PREKEY_ID));
},
reject
)
});
}

Categories

Resources