Error handling with async /await in TypeScript
The problem
We don’t know whether function A or B threw the error.
function getTrueOrFalse() { return !Math.floor(Math.random() * Math.floor(2)); } function A() : Promise<string> { return getTrueOrFalse() ? Promise.resolve("A is good") : Promise.reject("Backend failure"); } function B(val) : Promise<string> { return getTrueOrFalse() ? Promise.resolve("B is good") : Promise.reject("Backend failure"); } async function runAll() : Promise<any> { try { const resultA = await A(); console.log("A: " + resultA); const resultB = await B(resultA); console.log("B: " + resultB); } catch(exc) { console.log(exc + ". Exception! But from where?"); } } runAll();
To fix this we could nest even more try and catch blocks, but that is ugly code.
Solution using multiple try..catch
That’s my favorite one. Looks clean! Take note that in this example resultA
must be visible in the second try-block, so we have to define resultA
above the first try-block.
function getTrueOrFalse() { return !Math.floor(Math.random() * Math.floor(2)); } function A() : Promise<string> { return getTrueOrFalse() ? Promise.resolve("A is good") : Promise.reject("Backend failure"); } function B(val) : Promise<string> { return getTrueOrFalse() ? Promise.resolve("B is good") : Promise.reject("Backend failure"); } async function runAll() : Promise<any> { let resultA; try { resultA = await A(); console.log("A: " + resultA); } catch(exc) { return console.log(exc + ". Exception from A"); } try { const resultB = await B(resultA); console.log("B: " + resultB); } catch(exc) { return console.log(exc + ". Exception from B"); } } runAll();
A solution without try..catch
In this solution we totally avoid using try..catch() by writing a small handle function that always returns a promise which contains the data in success case or the error in case of failure.
function getTrueOrFalse() { return !Math.floor(Math.random() * Math.floor(2)); } function A() : Promise<string> { return getTrueOrFalse() ? Promise.resolve("A is good") : Promise.reject("Backend failure"); } function B(val) : Promise<string> { return getTrueOrFalse() ? Promise.resolve("B is good") : Promise.reject("Backend failure"); } function handle(prom) { return prom.then((res) => [res]).catch((err) => [,err]); } async function runAll() : Promise<any> { const [resultA, errA] = await handle(A()); if(errA) { return console.log(errA + " from A"); } console.log("A: " + resultA); const [resultB, errB] = await handle(B(resultA)); if(errB) { return console.log(errB + " from B"); } console.log("B: " + resultB); } runAll();
But this is also somewhat ugly, because you have to wrap all your async functions into a handler.