I am writing a unit test to ensure that catch works in the following function:
function myFunction(){
const myPromises = Promise.all(getMyPromises())
return Promise.all(myPromises
.then( objArray => {
return Promise.all(Promise.map(objArray,
doSomethingWithPromises()
)).then(response => {
getSomeMorePromises()
return response;
})
.catch(err => {
doSomethingWhenErr();
});
})
) .catch(err => {
doSomethingWhenErr();
});
}
I am unable to catch the error from the inner catch in the outer therefor my unit test is failing.
I have tried excluding the catch from the inner function however that didn't work.
How do I ensure that if a promise is not resolved in the getSomeMorePromises() the error is returned and catched in the outer?
Promise chains are formed from promises that you return from your then() callbacks.
Merely creating a promise inside a then() callback doesn't link to it or handle its errors in any way.
You need to return those promises:
function myFunction() {
const myPromises = Promise.all(getMyPromises())
return myPromises.then(objArray => {
return Promise.all(objArray.map(getOtherPromise));
)).then(response => {
return Promise.all(getSomeMorePromises());
});
}
Related
In react-native, unhandled promises are silent. There used to be an "Unhandled Promises Rejection" warning, but now I don't see it anymore. Someone knows what happen to that?
I've discovered that putting .done() after a promise, will raise an error if the promise was rejected.
Suppose that I have a promise that is suppose to never be rejected, should I put .done after it, just to detect possible errors?
const asyncFunc = async () => {
const a = null;
return a.undefinedField;
}
<Button onPress={() => asyncFunc()} />
<Button onPress={() => asyncFunc().done()} />
I'm using react-native which uses promise (Github).
By done I'm referring the one explained here: Promise API Reference.
There was already a similar question here, but it's 5 years old and the react-native project changes very fast.
Promise does not have done function.
You can catch possible errors in catch and place things to do in regardless of success in finally.
For example:
const asyncFunc = () => {
return new Promise((resolve) => {
throw new Error('Error');
resolve('Result');
})
}
asyncFunc().then(res => {
console.log(res);
}).catch(e => {
console.error(e);
}).finally(() => {
console.log('This will be executed in regardless of success');
});
If you're using async - await, you can use try-catch as you can for other functions:
const asyncFunc = () => {
return new Promise((resolve) => {
throw new Error('Error');
resolve('Result');
})
}
const executeAsyncFunc = async () => {
try {
await asyncFunc();
} catch (e) {
console.error(e);
} finally {
console.log('This will be executed in regardless of success');
}
};
executeAsyncFunc();
I want to write a function used to wrap other functions so all errors are caught, including errors generated by a Promise rejection (which normally requires the .catch Promise method).
The goal is to be able to wrap functions so all runtime errors are handled. An example usage is a function we want to run, but that is optional and not part of the core business flow. If there is an error, we want to report it and later fix it, but we do not want it to stop program flow.
It should be able to wrap functions with any number of arguments, and return the same value as the original function, including if the original function returns a promise.
Am I handling all possible cases below? Is there a simpler way to do this?
const catchAllErrors = (fn) => (...args) => {
try {
const possiblePromise = fn(...args);
// Is it a promise type object? Can't use "instanceof Promise" because just
// for example, Bluebird promise library is not an instance of Promise.
if (typeof possiblePromise.catch === 'function') {
return Promise.resolve(possiblePromise).catch((error) => {
console.log('Caught promise error.', error);
});
}
return possiblePromise;
} catch (error) {
console.log('Caught error.', error);
}
};
// EXAMPLE USAGE
// Applying the wrapper to various types of functions:
const throwsErr = catchAllErrors((x, y) => {
throw `Error 1 with args ${x}, ${y}.`;
});
const promiseErr = catchAllErrors((a, b) => Promise.reject(`Error 2 with args ${a}, ${b}.`));
const noError = catchAllErrors((name) => `Hi there ${name}.`);
const noErrorPromise = catchAllErrors((wish) => Promise.resolve(`I wish for ${wish}.`));
// Running the wrapped functions:
console.log(throwsErr(1, 2));
promiseErr(3, 4).then((result) => console.log(result));
console.log(noError('folks'));
noErrorPromise('sun').then((result) => console.log(result));
Don't try to detect whether something is a promise or not yourself. Use the builtin thenable detection of promise resolution. You can use either the Promise constructor to catch the exception:
const catchAllErrors = (fn) => (...args) => {
return new Promise(resolve => {
resolve(fn(...args));
}).catch((error) => {
console.log('Caught error.', error);
});
};
or just go for async/await syntax:
const catchAllErrors = (fn) => async (...args) => {
try {
return await fn(...args);
} catch (error) {
console.log('Caught error.', error);
}
};
(If you used Bluebird anyway, you could also call its Promise.try method for this purpose)
How should I stop the promise chain in this case?
Execute the code of second then only when the condition in the first then is true.
var p = new Promise((resolve, reject) => {
setTimeout(function() {
resolve(1)
}, 0);
});
p
.then((res) => {
if(true) {
return res + 2
} else {
// do something and break the chain here ???
}
})
.then((res) => {
// executed only when the condition is true
console.log(res)
})
You can throw an Error in the else block, then catch it at the end of the promise chain:
var p = new Promise((resolve, reject) => {
setTimeout(function() {
resolve(1)
}, 0);
});
p
.then((res) => {
if(false) {
return res + 2
} else {
// do something and break the chain here ???
throw new Error('error');
}
})
.then((res) => {
// executed only when the condition is true
console.log(res)
})
.catch(error => {
console.log(error.message);
})
Demo - https://jsbin.com/ludoxifobe/edit?js,console
You could read the documentation, which says
Promise.then return a rejected Promise if the input function throws an error, or the input function returns a rejected Promise.
If you prefer, you could read the Promise A spec, in the section about then, where promise2 refers to the resulting promise:
If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.)
If you prefer, you could read the excellent 2ality blog:
then() returns a new promise Q (created via the constructor of the receiver):
If either of the reactions returns a value, Q is resolved with it.
If either of the reactions throws an exception, Q is rejected with it.
You could read the brilliant YDKJS:
A thrown exception inside either the fulfillment or rejection handler of a then(..) call causes the next (chained) promise to be immediately rejected with that exception.
You could move the chain into the conditional branch:
p.then((res) => {
if(true) {
return Promise.resolve(res + 2).then((res) => {
// executed only when the condition is true
});
} else {
// do something
// chain ends here
}
});
Just use something like: reject('rejected')
in the else of the first task.
P
.then((res) => {
if(true) {
return res + 2
} else {
reject('rejected due to logic failure' }
})
.then((res) => {
// executed only when the condition is true
console.log(res)
})
Alternatively u can also add a catch section to ur first task with .catch()
Hope this helps.
I'm using mondora/asteroid through a node app to use Meteor DDP via a promise pattern.
I have the following code I am rewriting from callback style, into a promise style, but am stuck on how to flatten it.
asteroid.call('Pony.search', { params })
.then(res => {
if (res.length === 1) {
// something
asteroid.call('Pony.finish', { params })
// this part feels wrong
.then(res => {
// something else
});
} else {
// nope
}
})
.catch(err => {
console.error(err);
});
There is a second asteroid.call & then inside the first promise response which is a promise. This part feels wrong, like it should be flat and not nested but I'm not sure how to get there.
edit:
Ended up using something like this (still not decided on whether to have the first then check for if length === 1 and potentially immediately reject it. Anyone know what is best practice on that?
asteroid.call('Pony.search', { params })
.then(res => res.length === 1 ? res : Promise.reject())
.then(res => asteroid.call('Pony.finish', { res[0].something }))
.then(res => {
// do something
})
.catch(err => {
// handle the no found
console.error(err);
});
Instead of nesting callbacks, chain promises together with .then()
A note:
I'm not sure what Promise library you are using, but the idea is to return a rejected promise from the first .then() if there is an error, otherwise you return a successful promise. The promise library will then handle the error handling for you, going to the catch block if there is a rejected promise.
asteroid.call('Pony.search', { params })
.then(res => {
res.length === 1 ? return asteroid.call('Pony.finish', { params }) : Promise.reject();
})
.then(res => {
//do stuff here
})
.catch(err => {
console.error(err);
});
edit:
The only issue is when you need to access both of the return values from the promises at the same time. When you flatten out a promise chain you lose access to the results from the previous promises.
You have a few options:
If you don't need the previous result then flatten out the promise chain like I did here
If you do need the previous value
2a. And you don't care about the ordering of execution then use Promise.all([promise1, promise2])
2b. And you do care about the ordering of the execution then you must use nested promises like you originally did.
If promises are nested, there is no difference between promises and callback.
Try changing your code to use promise chain:
asteroid.call('Pony.search', { params })
.then(res => {
if (res.length === 1) {
// something
let p1 = asteroid.call('Pony.finish', { params });
return p1;
}
else {
// nope
}
})
.then (function SomeThingElse(res2){
// something else
// res2 is return value from nested asteroid.call
})
.catch(err => {
console.error(err);
});
Since the first resolve handler returns a Promise p1, the invocation of the next functions in the chain (function SomeThingElse) is deferred until p1 is resolved.
Now expanding this example:
asteroid.call('Pony.search', { params })
.then(res => {
if (res.length === 1) {
// something
let p1 = asteroid.call('Pony.finish', { params });
return p1;
}
else {
// nope
}
})
.then (function SomeThingElse(res2){
// something else
// res2 is return value from nested asteroid.call
})
.then (function AnotherFunc(res3){
})
.catch(err => {
console.error(err);
});
If SomeThingElse returns a Promise, invocation of AnotherFunc is delayed until that promise is resolved.
If SomeThingElse does not return a Promise, AnotherFunc would be invoked immediately with the same parameters as 'SomeThingElse' received. In other words, both SomeThingElse and AnotherFunc are invoked when p1 is resolved.
This is probably a silly question, but mid promise chain, how do you reject a promise from inside one of the then functions? For example:
someActionThatReturnsAPromise()
.then(function(resource) {
return modifyResource(resource)
})
.then(function(modifiedResource) {
if (!isValid(modifiedResource)) {
var validationError = getValidationError(modifiedResource);
// fail promise with validationError
}
})
.catch(function() {
// oh noes
});
There's no longer a reference to the original resolve/reject function or the PromiseResolver. Am I just supposed to add return Promise.reject(validationError); ?
Am I just supposed to add return Promise.reject(validationError);?
Yes. However, it's that complicated only in jQuery, with a Promise/A+-compliant library you also could simply
throw validationError;
So your code would then look like
someActionThatReturnsAPromise()
.then(modifyResource)
.then(function(modifiedResource) {
if (!isValid(modifiedResource))
throw getValidationError(modifiedResource);
// else !
return modifiedResource;
})
.catch(function() {
// oh noes
});
Use Promise.reject
Like this
function test()
{
return new Promise((resolve, reject) => {
resolve(null)
}).then(() => console.info('yes')).then(() => {
return Promise.reject('hek');
})
}
test().then(() => console.info('resolved')).catch(() => console.info('rejected'))