Catching Errors in JavaScript Promises with a First Level try ... catch
With the new async/await syntax you can achieve this. Please note that at the moment of writing this is not supported by all browsers, you probably need to transpile your code with babel (or something similar).
// Because of the "async" keyword here, calling getSomeValue()
// will return a promise.
async function getSomeValue() {
if (somethingIsNotOk) {
throw new Error('uh oh');
} else {
return 'Yay!';
}
}
async function() {
try {
// "await" will wait for the promise to resolve or reject
// if it rejects, an error will be thrown, which you can
// catch with a regular try/catch block
const someValue = await getSomeValue();
doSomethingWith(someValue);
} catch (error) {
console.error(error);
}
}
handle promise error first level inside try catch
If you want to run it concurrently with your loop and properly handle errors from both (which necessarily includes waiting for both), then Promise.all
is your only choice:
async function test() {
try {
await Promise.all([
testPromise(10),
(async function() {
for (let i=0; i<10000; i++) {
…
}
…
}())
]);
} catch(err) {
console.log(err);
}
}
Why doesn't the try catch block catch the promise exception?
I can see two possible aspects to your question:
The error being thrown in
test
is in the synchronous (not asynchronous) part oftest
. Why is it a promise rejection rather than a synchronous exception?The promise from
test()
is being rejected, why doesn'tcatch
catch that rejection?
#1 - Why is it a rejection?
Because even if anasync
function throws during the synchronous portion of its work, that just rejects the promise it returns, it doesn't raise a synchronous error. It's just a design decision made during the design of async
functions, and a smart one — if it were a synchronous error when throwing in the synchronous part of the function but a promise rejection after that, it would be chaotic and hard to understand. So it's simple: throwing in an async
function always rejects its promise.(This is consistent with how the promise constructor treats the executor callback you pass into it. When you do new Promise((resolve, reject) => /*...*/})
, the promise constructor calls your function synchronously, but if you throw during the call, it uses what you throw to reject the promise rather than allowing it to continue as a synchronous exception.)
#2 - Why isn't rejection caught by the catch
block?
Because you're not using await
. Promise rejections are only exceptions when you are await
ing the promise. If you use the promise methods then
/catch
/finally
to attach handlers, rejection is handled by calling the rejection handler, not by the exception mechanism.So either use promise methods to attach fulfillment and rejection handlers:
test()
.then(result => {
// ...use `result` here...
})
.catch(error => {
// ...handle/report error here...
});
Or use await
in an async
function (or at the top level of a module if you have top-level await
in your enviroment):// In an `async` function (or the top level of a module in cutting-edge environments)
try {
const result = await test();
// ...use `result` here...
}
catch(err){
// ...handle/report error here...
}
Why a promise reject is not catched within a try catch block but produces an Uncaught error?
Technical reasons behind:
HostPromiseRejectionTracker is an implementation-defined abstracthttps://www.ecma-international.org/ecma-262/10.0/index.html#sec-host-promise-rejection-tracker
operation that allows host environments to track promise rejections.An implementation of HostPromiseRejectionTracker must complete
normally in all cases. The default implementation of
HostPromiseRejectionTracker is to unconditionally return an empty
normal completion.
Basically javascript engines can freely implement this spec. In the case of browsers you don't get the Error captured inside the try/catch because the error is not emitted where you think it should be. But instead it's tracked with this special event that throws the error in the console.
Also on nodejs, this event causes the process to exit if you have node set to exit on unhandled exceptions.
On the other side, if you instead use async/await, the rejection is treated like an 'error' in practical terms. Meaning that the newer async/await feature behaves in a different fashion showing that it is not only syntactic sugar for Promises.
https://tc39.es/ecma262/#sec-throwcompletion
In sum, if you use Promise.then you are forced to provide a reject argument or chain it with .catch to have the rejection captured or else it will reach the console and in case of nodejs to exit the process if configured to do so (I believe new nodejs does this by default).
But if you use the newer async/await syntax you not only have a concise code (which is secondary) but a better rejection handling because it can be properly nested in a try/catch and it will behave like an standard Error.
NodeJs Error handling promise error with in try catch block
Promises work asynchronously, making them execute outside of the scope of the catch
block you have here. This is why they have their own version of catch
-
promise
.then(function(data) {
//some business logic
return anotherPromise;
})
.then(function(data) {
//some other business logic
})
// this keeps chaining as long as needed
.catch(function(err) {
// You will get any thrown errors by any of the
// chained 'then's here.
// Deal with them here!
});
This is the recommended way. try catch vs promise chaining mechanism for error handing which one offers more flexibility?
Firstly, try... catch
is useful to catch mostly expected
errors (when you need to return some 4xxx error with additional info or even 200 with a business logic error code or something like that) rather than some unexpected errors that should lead to 500 HTTP errors. That said you need a global handler for all non-catched/unexpected errors that will log these errors and send the 500 error to a client.
global error handler
app.use(function (err, req, res, next) {
logger.error(err)
res.status(500).send('Unexpected error')
})
try/catch with an explicit transactionconst registerNewUser = async (req, res) => {
/*
code here for extracting info from req
*/
// here is some call to create a transaction in a DB
// it varies depending on a type of DB and a package you use to communicate with it
// we need to create a transaction BEFORE try/catch block
const transaction = await createTransaction();
try {
// we need to pass transaction to every method that modifies data in DB
// also depends on how to pass a transaction to underlying DB package methods
const users_data = await users.create_new_user({
email,
crypted_pwd,
salt,
first_name,
phone_code,
}, transaction);
const { id: user_id, first_name: user_first_name } = users_data;
// we need to pass transaction to every method that modifies data in DB
const customer_data = await customers.create_new_customer({
first_name,
last_name,
email,
}, transaction);
const {
tenant_id,
id: customer_id,
} = customer_data;
await clearCartFromRedis(merchant_canonical_name, req.token);
signUpWelcomeMailer(merchant_canonical_name, req.token, user_id);
// here we need to commit the transaction before we exit from this handler
await transaction.commit();
return res.status(200).json({
success: true,
access_token: req.token,
first_name: user_first_name,
});
} catch (error) {
// here we need to rollback the transaction before we exit from this handler
await transaction.rollback();
logger.log({ message: error.message, level: 'error' });
return res.status(500).send(error.message);
}
};
Secondly, in your case of several operations against some DB you clearly need to use a transaction mechanism to keep data consistent. Usually, it requires creating an explicit transaction and passing it to each query that should be executed as a one atomic operation that either executes correctly (then you need to commit a transaction) or fails (then you need to rollback a transaction). For that, you can use try...catch
to be able to commit or rollback a transaction at the end of a batch of operations. How do catch a reject from a function which contains a promise but does not return it
You can listen for the "unhandledrejection" event.
window.addEventListener("unhandledrejection", e => {
e.preventDefault(); // prevent error message
// handle...
// e.promise is the Promise that was rejected
// e.reason is the object passed to reject()
});
Even with Try{} Catch (e){} the script does not ignore the error - Uncaught (in promise) SyntaxError: Unexpected end of Json Imput
fetch
performs asynchronous operations and try/catch
can only catch errors that are originated when first running the code inside its corresponding block (your error is being thrown on your first .then()
callback). You can add a try/catch
wrapping response.json()
and it would work as you expect.
Alternatively, you can safely reformat your code as follows, leveraging promises catch
method:
function refresh_images() {
const selectedstages = document.getElementById("matches").selectedOptions[0].getAttribute("data-stages");
const selectedevents = document.getElementById("matches").selectedOptions[0].getAttribute("data-events");
fetch("testedropdown.json")
.then(response => response.json())
.then(data => {
console.log(data.Stages[selectedstages].Events[selectedevents].Eps);
})
.catch((err) => {
console.log(err);
// HANDLE ERRORS HERE.
});
}
More info here. Try and catch around promise
You don't say exactly how A()
fails. It could either throw or it could return an error result. I'll show a scheme for both. The key to a mix of sync and async is to always return a promise. This will give you a consistent interface for teh caller no matter how the function succeeds or fails.
If you are only worried about A()
throwing an exception and it doesn't return an error code, then you can do this:
function someFunction() {
try {
var result = A();
return B(result);
} catch(err) {
return Promise.reject(err);
}
}
someFunction().then(function(result) {
// code here to process the final result
}).catch(C);
If you also have the case where A()
can return an error code, then you can do this:function someFunction() {
try {
var result = A();
// check for error value
if (result < 0) {
throw result;
}
return B(result);
} catch(err) {
return Promise.resolve(err);
}
}
Note that both of these patterns avoid creating an extra promise if it isn't needed. They only create the extra promise when returning an error that occurred synchronously.The Bluebird promise library has a helper function for this particular circumstance called
Promise.method
. The utility of Promise.method()
is that it automatically wraps your function in a try/catch handler and if there are any synchronous exceptions thrown, it automatically turns them into returning a rejected promise. You could use it like this:var someFunction = Promise.method(function() {
var result = A();
// check for error condition
if (result < 0) {
throw result;
}
return B(result);
});
someFunction().then(function(result) {
// code here to process the final result
}).catch(C);
Related Topics
What Is the Most Efficient Way to Reverse an Array in JavaScript
Removing Duplicate Objects with Underscore for JavaScript
Why Do People Put Code Like "Throw 1; <Dont Be Evil>" and "For(;;);" in Front of JSON Responses
Google Map API - Multiple Icons in Wrong Spot
Difference Between Obtrusive and Unobtrusive JavaScript
Why Escape_JavaScript Before Rendering a Partial
Cross Domain Localstorage with JavaScript
Object Doesn't Support This Property or Method Rails Windows 64Bit
Razor MVC Populating JavaScript Array with Model Array
Node.Js Piping the Same Readable Stream into Multiple (Writable) Targets
What Is the JavaScript Mime Type for the Type Attribute of a Script Tag
How to Filter an Array in JavaScript
JSON Transfer of Bigint: 12000000000002539 Is Converted to 12000000000002540
Invoke a Callback at the End of a Transition