Exception handling in promise chain - javascript

I have a code with multiple promise chain as shown below
.then(function(response) {
//my code
})
.then(function(app) {
//my code
})
.then(function() {
//my code
})
Have added exception handling to each of them as shown below so that if one breaks the rest chain continues.
Is this the correct way of handling exception for multiple chain blocks, or any best practice can be followed to handle the exceptions so that the code execution doesn't break if one fails.
.then(function(response) {
//my code
})
.catch(e => {})
.then(function(app) {
//my code
})
.catch(e => {})
.then(function() {
//my code
})
.catch(e => {})

If your code can accommodate the error (whatever it is) that's occurring early on, this can be a reasonable way of doing it, but I'd always have to look twice at it in a code review because it's fairly unusual for the code to be able to just ignore errors like that. The code is roughly equivalent to:
try {
//my code
} catch (e) {
}
try {
//my code
} catch(e) {
}
try {
//my code
} catch(e) {
}
...but using promises instead. So it's a bit suspect, but can be correct, for the same reasons the above is a bit suspect but can be correct if you need to do a series of things, one at a time, and have each of them done even if the previous one fails.
Beware that it means app in the subsequent fulfillment handler will be undefined:
.then(function(response) {
//my code
})
.catch(e => {})
.then(function(app) { // <=== `app` is `undefined` here
//my code
})
.catch(e => {})
.then(function() {
//my code
})
.catch(e => {})

Answer posted before, is correct.
I just want to insert my 2 cents.
You can have some fun with one helper function (or use something more fancy like whole Either monad thingy from fp-ts package)
const run = async (fn) => {
try {
const result = await fn()
return [result, null]
} catch (err) {
return [null, err]
}
}
and write code without try\catch or .then\.catch
const [response, error] = await run(() => fetch('asdfasdf'))
const app = buildApp(response.ok ? await response.json() : { fallback: 'data' })
const [appResponse, appError] = await run(async () => {
await app.compileTemplate()
return app.buildResponse()
})
if (appResponse) {
// ...
}
Or more useless approach, you can throw a custom error, so your first .catch block will be able to do something.
class AppFromResponseError extends Error {
constructor(message, fallbackApp) {
super(message)
this.name = "ResponseError"
this.app = "fallbackApp"
}
}
builder
.then(function(response) {
if (response.ok !== true)
throw new AppFromResponseError('not ok', minimalApp)
})
.catch(e => {
if (e.app) return e.app
})
.then(app => { /* some kind of app will be here */})

Related

Mocha Dynamic test generation in before block not getting executed

As suggested in this post , I tried the steps to create dynamic tests , but I see the actual test(test.getMochaTest()in my below implementation) not getting executed. What's that I'm missing here, the call on test.getMochaTest() does not get executed in the before block.
describe('Dynamic Tests for Mocha', async () => {
let outcome ;
before(async() => {
await init().catch(() => console.error('Puppeteer environment initialization failed'));
return collectTests().then(async(collectTests) => {
console.info('4.Executing tests :');
describe('Dynamic test execution', async() => {
collectTests.forEach(async(test) => {
console.info(`\tModule under test : ${test.name}`);
// impl. of test.getMochaTest() DOES NOT get executed.
it(test.name, async() => outcome = await test.getMochaTest().catch(async () => {
console.error(`error while executing test:\t${test.name}`);
}));
});
}) ;
});
});
after(async () => {
console.info('5. Exiting tests...');
await HelperUtils.delay(10000).then(async () => { await browser.close(); });
console.log('executing after block');
});
it('placeholder', async() => {
await
console.log('place holder hack - skip it');
});
});
Array of tests is returned here :
async function collectTests():Promise<Array<ITest>> {
console.info('3.Collecting tests to execute ...');
testArray = new Array<ITest>();
const login:ITest = new SLogin('Login Check');
testArray.push(login);
return testArray;
}
The below implementation of getMochaTest in SLogin -> does not get executed .
export default class SLogin extends BTest implements ITest {
constructor(name: string) {
super(name);
}
async getMochaTest():Promise<Mocha.Func> {
return async () => {
console.log('Running Login check');
expect(true).to.equal(true);
};
}
}
It doesn't look like you're actually invoking the test.
Calling test.getMochaTest() only returns the async test function in a Promise, it doesn't execute it. So your catch block is catching errors while obtaining the function, not while executing it.
Breaking it out across multiple lines will hopefully make things clearer.
Here's what your code sample does. Notice it never executes the returned test function:
it(test.name, async () => {
const testFn = await test.getMochaTest().catch(() =>
console.error(`error while ***obtaining*** test: \t${test.name}`));
// oops - testFn never gets called!
});
And here's a corrected version where the test actually gets called:
it(test.name, async () => {
const testFn = await test.getMochaTest().catch(() =>
console.error(`error while ***obtaining*** test: \t${test.name}`));
const outcome = await testFn().catch(() =>
console.error(`error while ***executing*** test: \t${test.name}`));
});
Note: I wrote it that way with await and catch() to better match the format of your code sample. However, it's worth pointing out that it mixes async/await and Promise syntax. More idiomatic would be to catch errors with a try/catch block when using async/await.

ReactJS - Javascript try/catch shows an error in the console

I'm pretty new on React and I'm learning this language nowadays.
For this purpose I'm building a test project on which I try to encounter the main classical issues and looking to solve it.
For most of React developpers, there are no difficulties in following code but I will give a few details for better comprenhension.
I have a portion of javascript code that is returning a list of articles from a Symfony Backend API only if user is authorized for getting it (Authorization via JWT will be done later). A getArticles function returns a Promise that tries to get the articles from the Symfony backend inside a try {} catch (error) {} block.
Voluntarily, Authorization token is not send to trigger an error in the query.
As the axios.get is located inside a try {} catch (error) {} block, I am surprised that an error appears in the console for the request. It doesn't impact the behavior but it is not very clean to have these errors in the console.
My question(s) :
Why an error appears in the console while the code is inside a try/catch ? To get a cleaner app behavior, is there a way to avoid having this error in the console ? I have found other React try/catch issues but I didn't deduct the similarity with my issue. Am I missing something ?
Thanks in advance ;-)
I am aware that my code could be refactored, do not hesitate to suggest any good practice
componentDidMount(){
/*To be prepared to attach JWT token*/
axios.interceptors.request.use(req => {
return req;
});
const getArticles = async() => { return new Promise( (resolve, reject)=> {
try{
const data = axios.get('https://xxxxx/api/articles');
resolve(data);
} catch (err) {
reject(err);
}
});
}
getArticles().then(res => {
const articles = res.data.data.items;
this.setState( {errorOnArticlesLoading:false, articles: articles } );
})
.catch(error => {
this.setState( {errorOnArticlesLoading:true} );
});
}
You can try in this way and Async functions itself returns a promise, you don't need to return a new Promise manually.
async componentDidMount() {
try {
/*To be prepared to attach JWT token*/
axios.interceptors.request.use(req => req);
const getArticles = async () => {
try {
const data = axios.get('https://xxxxx/api/articles');
this.setState({ errorOnArticlesLoading: false, articles: data.data.items });
} catch (err) {
this.setState( {errorOnArticlesLoading:true} );
}
};
await getArticles()
} catch(err) {
console.log('Handled root error')
}
}
It seems that there are no solutions to avoid the 401 http error code in the console because it it printed by Chrome itself: See discussion here. So the following code cannot avoid the 401 error status to be printed in the console.
componentDidMount(){
/*To be prepared to attach JWT token*/
axios.interceptors.request.use(req => {
return req;
});
const getArticles = async() => {
const data = await axios.get('https://xxxx/api/articles');
return data;
}
getArticles().then(res => {
const articles = res.data.data.items;
this.setState( {errorOnArticlesLoading:false, articles: articles } );
})
.catch(error => {
this.setState( {errorOnArticlesLoading:true} );
});
}

how to differentiate error's source based on stage in Promise chain

I have 2 callback that both call API and return Promise. They should go sequentially. Let's name them verifyThing and updateThing.
So without error handling it would be as easy as
verifyThing()
.then((id) => updateThing(id))
But now error handling comes. Suppose I need to display different error message once verifyThing fails or updateThing. Also obviously I don't need to call updateThing if verifyThing fails.
So far I've tried with custom Error:
class VerifyError extends Error {};
verifyThing()
.catch(e => {
message("cannot verify");
throw new VerifyError();
})
.then((id) => updateThing(id))
.catch(e => {
if (e instanceof VerifyError) return;
message("cannot update");
})
Unfortunately custom Error check does not work with Babel 6.26 we use. As a dirty patch I can throw magic string instead of Error subclass, but ESLint rules are for a reason, right?
So finally I have got working variant. But I believe it has worse readability(because of nesting):
verifyThing()
.catch(e => {
message("cannot verify");
throw e;
})
.then((id) =>
updateThing(id)
.catch(e => {
message("cannot update");
})
)
Is there any other way to handle all the logic in the same chain?
It seems like the actual behavior you want can be achieved by reordering your last example:
verifyThing().then(id =>
updateThing(id).catch(e => {
message("cannot update");
})
).catch(e => {
message("cannot verify");
})
The outer catch() will only catch errors from verifyThing() since errors from updateThing(id) have been handled by the inner catch(). In addition, you're always left with a resolved promise instead of a rejected one, which is appropriate since both types of errors have been handled already.
To avoid the appearance that error handling is not close to the source, move the inner portion to a helper function:
function tryUpdateThing (id) {
return updateThing(id).catch(e => {
message("cannot update");
});
}
verifyThing().then(
tryUpdateThing
).catch(e => {
message("cannot verify");
});
Whether or not this is acceptably readable, I'll leave up to you to decide.
If async/await is an option, then:
async function() {
let id;
try {
id = await verifyThing();
await updateThing(id);
} catch(e) {
message(id === undefined ? "cannot verify" : "cannot update");
throw e;
}
}
This assumes that when verifyThing() fulfills, it will resolve with a defined value.

getting value from a chained promises

im really new to this of promises and im getting headaches trying to understand this, so now im trying to get an answer from a method that returns a promise, then i catch the value in a conditional and make some other operations
let addService = async(req, res) => {
checkCategoryExists(param).then(result => {
if(result){
// code here
}
}).catch(err => console.log(err));
}
let checkCategoryExists = async(param) => {
let docs = db.collection(collectionName).doc(param);
docs.get()
.then(categoryDoc => {
if(categoryDoc.exists){
if(categoryDoc.data().param== param){
return true;
}
} else {
return false;
}
})
.catch(err => false);
}
the method "checkCategoryExists" is a query to a firestore db. When i tried to check if result variable is true or false, it happens to be undefined. its not with ".then()" that i get to catch the value from the returned promise? if someone can help me, thanks in advance
So as mentioned above I think your issue is based on not returning the results of your document search so both examples below handle that.
I also noticed that you were using async tags on your functions but not ever using await so I wanted to give examples of both ways of doing it in case you wanted to use Async/Await but weren't certain how.
In the promise chain example I'm relying on the syntax of arrow functions to create the returns (no {} means return right side of equation) since none of your given code requires data manipulation before return (if your actual code needs that you should of course use brackets and remember your return statement :D )
If you choose to use Async/Await you can structure the code much more closely to synchronous examples and use try catch statements. Sometimes I find this syntax more clear if I have to do a lot of processing/manipulation before returning a result.
Good Luck :)!
// Promise Chains
const addService = (req, res) =>
checkCategoryExists(param)
.then(result => /* do stuff */)
.catch(err => console.error(err.message));
const checkCategoryExists = param =>
db.collection(collectionName.doc(param)
.get()
.then(categoryDoc =>
Promise.resolve((categoryDoc.exists && categoryDoc.data().param === param))
);
// OR using Async/Await
const addService async (req, res) => {
try {
const catExists = await checkCategoryExists(param);
if (!catExists) {
throw new Error('error code');
}
// Do Stuff.
} catch (error) {
console.error(error);
}
};
const checkCategoryExists = async param => {
try {
const docs = await db.collection(collectionName)
.doc(param)
.get();
return (docs.exists && docs.data().param === param);
} catch (error) {
console.error(error)
}
}

testing promises causing undefined values

I am getting this error when I am testing my code:
1) Sourcerer Testing: getStatusCode :
Error: Expected undefined to equal 200
I'm not sure why I am getting undefined in my tests but when I run the code I get 200. It might be from not handling promises properly
Test code:
import expect from 'expect';
import rp from 'request-promise';
import Sourcerer from './sourcerer';
describe("Sourcerer Testing: ", () => {
let sourcerer = new Sourcerer(null);
const testCases = {
"https://www.google.com": 200,
// "www.google.com":
};
describe("getStatusCode", () => {
it("", () => {
for (let testCase in testCases) {
sourcerer.setSourcererUrl(testCase);
expect(sourcerer.url).toEqual(testCase);
expect(sourcerer.getStatusCode()).toEqual(testCases[testCase]);
}
});
});
});
code:
import rp from 'request-promise';
export default class Sourcerer {
constructor(url) {
this.options = {
method: 'GET',
url,
resolveWithFullResponse: true
};
this.payload = {};
}
setSourcererUrl(url) {
this.url = url;
}
getSourcererUrl() {
return this.url;
}
analyzeSourcePage() {
rp(this.options).then((res) => {
console.log(res);
}).catch((err) => {
console.log("ERROR");
throw(err);
});
}
getStatusCode() {
rp(this.options).then((res) => {
console.log(res.statusCode);
return res.statusCode;
}).catch((err) => {
console.log("STATUS CODE ERROR");
return 0;
});
}
}
getStatusCode doesn't return anything. And it should return a promise:
getStatusCode() {
return rp(this.options)...
}
The spec will fail in this case, because it expects promise object to equal 200.
It is even more complicated because the spec is async and there are several promises that should be waited before the spec will be completed. It should be something like
it("", () => {
let promises = [];
for (let testCase in testCases) {
sourcerer.setSourcererUrl(testCase);
let statusCodePromise = sourcerer.getStatusCode()
.then((statusCode) => {
expect(sourcerer.url).toEqual(testCase);
expect(statusCode).toEqual(testCases[testCase]);
})
.catch((err) => {
throw err;
});
promises.push(statusCodePromise);
}
return promises;
});
co offers an awesome alternative to Promise.all for flow control:
it("", co.wrap(function* () {
for (let testCase in testCases) {
sourcerer.setSourcererUrl(testCase);
expect(sourcerer.url).toEqual(testCase);
let statusCode = yield sourcerer.getStatusCode();
expect(statusCode).toEqual(testCases[testCase]);
}
});
Disclaimer: I wouldn't run a for-loop in a single it(), since I want to know which iteration failed. granted that there are ways to achieve that, but that is another story. Also, this very much depends on you test runner, but here is some rules of thumb I find useful.
But for what you have asked, the test should not evaluate until the promise is resolved. sometimes (e.g. in mocha), that means returning the promise from the it() internal function. sometimes, it means getting a done function and calling it when you are ready for the test to evaluate. If you provide more info on your test framework, I may be able to help (others certainly would be)

Categories

Resources