Promise.reject in .then() returning undefined - javascript

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));

Related

Promise rejection not getting catched

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

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()

Javascript not catching error using fetch

I have an authService which has the following fetch
export const handleForm = () => {
return fetch('http://localhost:5000/login')
.then(response => {
if (!response.ok) {
throw new Error("Email or password incorrect");
} else {
return response.json();
}
})
.then(json => json)
.catch(error => error);
};
I'm calling the method from my controller:
form.onsubmit = function (e) {
handleForm(e)
.then(response => {
console.log(response);
//setAuthenticatedUser(response);
// setDomUser(response.user);
})
.catch(error => {
console.log(error);
document.querySelector('#error').innerHTML = error;
})
};
I've tried a few things to get the error in my catch, in the controller
But I keep getting the response callback fired.
The error from my authService isn't being caught
I've Tried
throw new Error();
Promise.reject();
See Promise.prototype.catch():
Syntax Section
p.catch(onRejected);
[...]
The Promise returned by catch() is rejected if onRejected throws an error or returns a Promise which is itself rejected; otherwise, it is resolved.
Using .catch(error => error) as a callback returns the exception. So it neither "throws an error" nor "returns a Promise which is itself rejected". The promise returned by it is thus resolved, not rejected.
Simply remove .catch(error => error) and it will work. And when you're at it, also remove .then(json => json) as it is useless.

How do I fix a race condition in a nested Promise in Node.js? [duplicate]

I'm creating an API using Node.js/TypeScript running Express. Below is an excerpt from my get method. An error is being triggered in the format method, which throws an error that is caught by the promise, but not propagated to the parent promise after a throw:
this.getModel(objectName).findAll(queryParameters).then(function(databaseObjects) {
for (let databaseObject of databaseObjects) {
var jsonObject = {};
//console.log("Database object: ");
//console.log(databaseObject);
transform.baseFormat(databaseObject, jsonObject)
.then(() => transform.format(databaseObject, jsonObject))
.then(() => {
res.locals.retval.addData(jsonObject);
}).catch((e) => {
console.log("Caught error during format of existing object: ");
console.log(e);
throw e;
});
}
})
.then(() => {
if (metadata) {
this.metadata(objectName, false, transform, res.locals.retval);
delete queryParameters.limit;
delete queryParameters.offset;
console.log("RUNNING METADATA COUNT: ");
this.getModel(objectName).count(queryParameters).then(function(count) {
res.locals.retval.setMetadata("records", count);
return next();
}).catch(function(e) {
this.error(e, res);
return next();
});
} else {
console.log("NO METADATA");
return next();
}
})
.catch((e) => {
// TODO: Move status into error() function
console.log("500 Error on GET");
console.error(e);
res.locals.retval.addError(ErrorCode.InternalError, e);
res.status(ErrorCode.InternalError).send(res.locals.retval);
return next();
});
Here's the output:
(node:8277) Warning: a promise was created in a handler at /Library/WebServer/adstudio/dist/server.js:555:51 but was not returned from it, see
at Function.Promise.bind (/Library/WebServer/adstudio/node_modules/bluebird/js/release/bind.js:65:20)
Caught error during format of existing object:
Test Error
END FUNCTION HAS BEEN REACHED!
Then the request fails to finish.
I've read a lot on Promises and I haven't been able to find an issue/solution similar to mine.
http://bluebirdjs.com/docs/warning-explanations.html
http://taoofcode.net/promise-anti-patterns/
https://www.reddit.com/r/javascript/comments/4bj6sm/am_i_wrong_to_be_annoyed_with_promise_error/
https://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html
Chained promises not passing on rejection
http://wiki.commonjs.org/wiki/Promises/A
https://promisesaplus.com/
Running inside that for-loop is not asynchronous, so your promise is resolving basically as soon as the loop finishes, yet before all your formatting finishes.
Use a promise control flow, like bluebird's Promise.each which is serial or just Promise.all. Then any exceptions will be caught.
this.getModel(objectName).findAll(queryParameters).then(function (databaseObjects) {
var promises = databaseObjects.map(databaseObject => {
var jsonObject = {}
// console.log("Database object: ");
// console.log(databaseObject);
return transform.baseFormat(databaseObject, jsonObject)
.then(() => transform.format(databaseObject, jsonObject))
.then(() => {
res.locals.retval.addData(jsonObject)
}).catch((e) => {
console.log('Caught error during format of existing object: ')
console.log(e)
throw e
})
})
return Promise.all(promises)
})
.catch((e) => {
// TODO: Move status into error() function
console.log('500 Error on GET')
console.error(e)
res.locals.retval.addError(ErrorCode.InternalError, e)
res.status(ErrorCode.InternalError).send(res.locals.retval)
return next()
})

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