How to handle errors from setTimeout in JavaScript? - javascript

Simple question about try/catch for function in setTimeout
try {
setTimeout(function () {
throw new Error('error!');
}, 300)
} catch (e) {
console.log('eeee!')
console.log(e)
}
Why doesn't catch block work?
What can I read about this?
P.S: the question is about possibility of handling errors like this. Don't answer about promises.

Functions scheduled to run with setTimeout are executed in the main loop, outside the body of code that originated them.
To handle errors, put the try-catch inside the setTimeout handler:
setTimeout(function () {
try {
throw new Error('error!');
} catch (e) {
console.error(e);
}
}, 300)
If you need to access the Error object from block that called setTimeout, use Promises:
const promise = new Promise((resolve, reject) => {
setTimeout(function () {
try {
throw new Error('error!');
resolve(); // if the previous line didn't always throw
} catch (e) {
reject(e)
}
}, 300)
})
promise
.then(result => console.log("Ok " + result))
.catch(error => console.error("Ouch " + error))
This example above is not the most elegant way of handling the case with a Promise. Instead, implement a delay(ms) function like this:
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
Then call
delay(300).then(myFunction).catch(handleError)

You can find good explanation in this Node.js official doc.
The problem is that when the callback of your setTimeout() function executes the try { } catch(err) { } block is already exited. Also notice that the callback can crash Node.js process.
However if you want to handle the errors in the callback of setTimeout() function, then you can listen them by using process global EventEmitter object
process.on('uncaughtException', function(err){
console.log(err)
})

Because the catch block lexically surrounds the setTimeout call but that is not the function that throws.
The direct translation, is
setTimeout(function () {
try {
throw new Error('error!');
} catch (e) {
console.log('eeee!');
console.log(e);
}
}, 300);

A bit strange solution, but sometimes it would be useful maybe...
function globalErrorHandler(e) {
console.warn('eeee!')
console.warn(e);
}
const _setTimeoutOriginal = setTimeout;
setTimeout = function(callback, timeout) {
const args = Array.from(arguments).slice(2);
_setTimeoutOriginal(function() {
try {
callback.apply(this, args);
} catch (e) {
globalErrorHandler(e);
}
}, timeout);
};
setTimeout(function() {
throw new Error('error!');
}, 300)

Related

why it didn't catch the rejected promise? [duplicate]

This question already has answers here:
How to .catch a Promise.reject
(4 answers)
How to use try/catch, promise catch and async function correctly?
(1 answer)
Closed 7 months ago.
I was wondering why the try-catch block couldn't catch the promise rejected from foo.
Frustrated, need help. Thanks in advance.
function addTask(task) {
setTimeout(() => {
task.resolver()
}, 1000)
}
function foo() {
return new Promise((resolve, reject) => {
const task = {
resolver: function() {
reject('task failed')
}
}
addTask(task)
})
}
function test() {
try {
foo()
} catch (e) {
console.error(e)
}
}
// Uncaught (in promise) task failed
test()
It's the same reason that if you had:
const result = foo();
Then you would have a promise stored in result and not the settled value of the promise.
The asynchronous code is started, then the calling function continues.
Sometime later the promise is rejected, but by then it is too late for the code in test() to catch it.
If you had awaited foo(), which would require that test() be async, then it would be caught.
Otherwise you could need to use foo().catch(...).
Promises are asynchronous but you are not awaiting. It means code continues. So you have to await or you have to use then().catch(). Something like this:
function addTask(task) {
setTimeout(() => {
task.resolver()
}, 1000)
}
function foo() {
return new Promise((resolve, reject) => {
const task = {
resolver: function() {
reject('task failed')
}
}
addTask(task)
})
}
function test() {
foo().then(console.log).catch(console.error);
}
// Uncaught (in promise) task failed
test()
To simplify your example:
function foo() {
return new Promise((resolve, reject) => setTimeout(reject, 1000))
}
function test() {
try {
foo()
} catch (e) {
console.error("Error");
return;
}
console.log("OK!");
}
test()
will print "OK!" indeed have Node.js raise an UnhandledPromiseRejection, as that foo() invocation is just "throwing away" the promise result value. The catch() would only catch synchronous exceptions, not promise rejections.
Turning the function into async function test() and awaiting on foo() will correctly output Error and no unhandled rejection.
Add await before foo() call and async before test function declaration
like below
setTimeout(() => {
task.resolver()
}, 1000)
}
function foo() {
return new Promise((resolve, reject) => {
const task = {
resolver: function() {
reject('task failed')
}
}
addTask(task)
})
}
async function test() {
try {
await foo()
} catch (e) {
console.error(e)
}
}
// Uncaught (in promise) task failed
test()`

How to throw an error inside a setInterval that is inside a function that is inside a try block?

Consider the code below:
async function why(){
try {
function noWait(callback) {
callback();
}
async function waitForTimer(callback) {
var timer = setInterval(()=>{
clearInterval(timer);
callback()
}, 10);
}
waitForTimer(()=>{
throw "this is error??"
});
noWait(()=>{
throw"this is error!!"
});
} catch (err) {
console.log(err);
}
}
why()
How can I throw errors inside the setInterval function and get the caught in the catch block?
The error thrown in the noWait() callback works but in the waitForTimer() callback does not, why is this so?
It throws an error but it is not being caught correctly (because the error is not being thrown within the try block). One alternative is to try and catch in your callback.
function why() {
function noWait(callback) {
try {
callback();
} catch (e) {
//error here
}
}
function waitForTimer(callback) {
var timer = setInterval(() => {
try {
clearInterval(timer);
callback()
} catch (e) {
//caught error here
}
}, 10);
}
waitForTimer(() => {
throw "this is error??"
});
noWait(() => {
throw "this is error!!"
});
}
But of course this is pretty bad.
setInterval can be kind of tricky with builtin async features. It's not a good fit for Promise since you could resolve/reject multiple times. Possible alternatives would be to use an Event Emitter, use an Observable library (like RxJS) or maybe even writing an async iterator.
In this example since you are using setInterval as a setTimeout basically... you can just wrap it into a promise.
let timeout = ms => new Promise(res => setTimeout(res, ms));
async waitForTimer function(){
await timeout(10);
callback();
}
Then you can catch it if you await waitForTimer:
let timeout = ms => new Promise(res => setTimeout(res, ms));
async function why() {
try {
function noWait(callback) {
callback();
}
async function waitForTimer(callback) {
await timeout(10);
callback();
}
await waitForTimer(() => {
throw "this is error??"
});
noWait(() => {
throw "this is error!!"
});
} catch (err) {
console.log(err);
}
}
why()
You can still catch the other like you do (you just won't caught both unless you move them into separate try blocks).
The timeout function can also be written with setInterval if you wish but it's kind of unnecessary.
let timeout = ms => new Promise(res => {
let timer = setInterval(() => {
clearInterval(timer);
res();
}, ms);
});
So, you're not catching the error is because code normally runs synchronously, one line at a time. But waitForTimer is an async function, which means it will be called, but run in the background. Anything you want to catch asynchronously needs to be wrapped in an async function or using Promises.
In this case, why is already an async function! So you can do:
async function why(){
try {
function noWait(callback) {
callback();
}
async function waitForTimer(callback) {
var timer = setInterval(()=>{
clearInterval(timer);
callback()
}, 10);
}
await waitForTimer(()=>{
throw "this is error??"
});
noWait(()=>{
throw"this is error!!"
});
} catch (err) {
console.log(err);
}
}
why()
Or
async function why(){
try {
function noWait(callback) {
callback();
}
async function waitForTimer(callback) {
var timer = setInterval(()=>{
clearInterval(timer);
callback()
}, 10);
}
waitForTimer(()=>{
throw "this is error??"
}).catch(err => console.log(err));
noWait(()=>{
throw"this is error!!"
});
} catch (err) {
console.log(err);
}
}
why()
For more info: How do I catch thrown errors with async / await?

Proper way to 'recover' from a failed promise?

I'm fairly new to JS development and I've recently discovered the concept of DRY (Don't Repeat Yourself), which has helped me clean up my code a lot.
I have the following type of issue in a few places throughout my project and I'm struggling to think of a way to improve it whilst maintaining the principles of readability and of not repeating code.
if (something) {
doPromise().then(() => {
doSomething()
}).catch(e => {
doThisInstead()
})
} else {
doThisInstead()
}
The crux of it is that I need to execute doThisInstead() or whatever function / in-line code is there whenever either the if statement goes to the else block, or when the promise goes to the catch block, and in this particular instance, I have no way of knowing that the promise will go to the catch block before it is attempted.
Writing code like this can quickly become messy, so I'd appreciate any tips. Many thanks!
You might be looking for if-else flow in promise (bluebird), just with catch instead of then:
(something
? doPromise().then(() => {
doSomething()
})
: Promise.reject()
).catch(e => {
doThisInstead()
})
Written with async/await, it would be
try {
if (!something)
throw new Error("something is wrong")
await doPromise();
await doSomething();
} catch(e) {
await doThisInstead();
}
An alternative that does not rely as much on exceptions would be
if (!something || await doPromise().then(doSomething).then(() => false, () => true)) {
doThisInstead();
}
If you use the _async / await _ syntax, you can await doPromise(), then run doThisInstead() if either something is falsey or an error occurred, this means only one call to doThisInstead() in your code.
This example will cause doPromise() to fail 50% of the time.
let something = true;
// Fail 50% of the time
function doPromise() {
return new Promise((resolve, reject) => setTimeout(Math.random() <= 0.5 ? resolve: () => reject(new Error("Some error")), 100));
}
function doSomething() {
console.log("doSomething()");
}
function doThisInstead() {
console.log("doThisInstead()");
}
async function test() {
errorOccurred = false;
if (something) {
try {
await doPromise();
doSomething();
} catch (e) {
errorOccurred = true;
}
console.log("doPromise: " + (errorOccurred ? "error occurred." : "ran successfully"));
}
// Run doThisInstead() if either an error occurred or something is falsey
if (!something || errorOccurred) {
doThisInstead();
}
}
test()
This can be solved with a Promise, using the following code:
function hypotheticalFunction() {
const doSomething = () => {
// stuff
}
const doThisInstead = () => {
// stuff
}
const doSomethingHandler = () => {
return new Promise((resolve,reject) => {
if (something) {
doPromise().then(() => {
doSomething();
resolve();
}).catch(() => {
reject();
})
} else {
reject();
}
})
}
doSomethingHandler().catch(doThisInstead);
}
hypotheticalFunction();

How to handle an unexpected error from a function after result is return from function in JavaScript?

While working on asynchronous functions in JavaScript. I discovered this problem.
A function needs to perform some asynchronous task, however that task doesn't make difference to function result. So function return its result, and after a while an exception is thrown from asynchronous function, however since control is already returned so exception goes unhanded.
Note: Not to change notify I'm intentionally throwing error like that, need to only handle this exception
Code is like:
function notify() { //To send notification
setTimeout( function() { //Just to simulate an asynchronous function
throw "Exception occurred"; //An exception from async function
},2000);
return "Notified";
}
try {
let result = notify();
console.log(result); //Notified
}
catch (error) { //Never comes to catch block.
console.log(error);
}
How can to catch this exception.
Tried using Promise, however it doesn't resolved with promise because even with promise, flow or function is removed from memory as calling function have received response.
Code using Promises
function notify() { //To send notification
return new Promise ( (resolve, reject) => {
setTimeout( function() { //Just to simulate an asynchronous function
throw "Exception occurred"; //An exception from async function
},2000);
resolve("Notified");
});
}
notify()
.then( result => {
console.log(result); //Notified
})
.catch( error => { //Never comes to catch block
console.log(error);
});
How can I catch exception in JavaScript programming?
I would wrap setTimeout in a promise as:
const delay = ms => new Promise(res => setTimeout(res, ms));
That allows you to write notify as:
async function notify() {
await delay(2000);
throw new Error();
}
notify().then(/*...*/).catch(/*...*/);
The trick here is that throw is inside of an asynchronous function and not as in your case inside of a callback.
var notify = function () {
anOddTask();
return "Notified";
}
async function anOddTask() {
try {
await setTimeout( function() {
},2000);
throw new Error("Exception occurred");
}
catch (err) {
console.log(err);
}
}
try {
let result = notify();
console.log(result); //Notified
}
catch (error) { //Never comes to catch block.
console.log(error);
}
This will immediately return "Notify" and handle the rejection at some later time. If you wish that "Notify" be returned as a resolved promise than just make notify() an async function.
var notify = async function () {
anOddTask();
return "Notified";
}

Node.js: Unhandled promise rejection - UnhandledPromiseRejectionWarning

I have a Node.js app. This app has a button that starts a process. The steps in that process return promises. I'm trying to chain these promises together. For some reason, I'm receiving an UnhandledPromiseRejectionWarning. However, in my mind, I've set this up correctly. My code looks like this:
var myButton = document.getElementById('myButton');
if (myButton) {
console.log('here');
myButton.addEventListener('click', executeAction('0'));
}
function executeAction(input) {
let param1 = 'A';
let promise = new Promise(function(resolve, reject) {
try {
executeStep1()
.then(result => executeStep2(param1, result, input))
.then(result => function(result) {
console.log('All done');
resolve(result);
})
.catch(err => reject(err))
;
} catch (ex) {
reject(ex);
}
});
return promise;
}
function executeStep1() {
let promise = new Promise(function(resolve, reject) {
try {
setTimeout(function() {
resolve('[something]');
}, 3000);
} catch (ex) {
reject();
}
});
return promise;
}
function executeStep2(p1, p2, p3) {
let promise = new Promise(function(resolve, reject) {
try {
setTimeout(function() {
console.log('step 2 has executed');
resolve('awesome!')
}, 3000);
} catch (ex) {
reject(ex);
}
});
return promise;
}
I've confirmed that the executeStep2 function runs to completion. I'm basing this in the fact that I can see "step 2 has executed" in the console window. However, to my surprise, I never see "All done" printed in the console window. Instead, I see the UnhandledPromiseRejectionWarning mentioned above. I don't understand two things about this result:
Why am I not seeing "All done" in the console? Shouldn't that function get executed after executeStep2 has resolved?
Where is the rejection coming from? I don't see anything that's rejecting this.
Thank you very much for your help!
executeStep1()
.then(result => executeStep2(param1, result, input))
.then(result => { // no function(result) here
console.log('All done');
resolve(result);
})
.catch(err => reject(err))
;
The error is generated from when you call the function(s) that uses promises:
myButton.addEventListener('click', executeAction('0'));
You need to catch rejections there also.
myButton.addEventListener('click', executeAction('0')
.catch((error) => console.log('ERROR', error));
The rejections are caught inside the functions, but not in the outer scope because executeAction('0') returns a promise, or it would but you are using it as a non-async function, so its creating a promise and then returning a pending promise without waiting for it to be resolved. That looks like what's causing the rejection, and its also not handled for the above reason.
This will fix it:
function executeAction(input) {
let param1 = 'A';
return new Promise(function(resolve, reject) {
try {
executeStep1()
.then(result => executeStep2(param1, result, input))
.then(result => function(result) {
console.log('All done');
resolve(result);
})
.catch(err => reject(err))
;
} catch (ex) {
reject(ex);
}
});
}
You should look into async/await. It can clean this code up significantly.
async function getSomething() {
try {
// throw 'Test error detected.'
return 'test'
}
catch (e) {
throw e
}
}
async function testing() {
try {
const sample = await getSomething()
return sample
} catch (e) {
throw e
}
}
testing()
.then((data) => console.log(data))
.catch((err) => console.log(err))
Run this above example, and then uncomment the throw. Use this pattern for maximum winning. Exceptions should be thrown to the surface level where the functions are called, displayed, used, rendered, etc. Functions should simply throw the error out.
This allows you to use error handling middleware, higher-order functions, listeners, logging, etc.

Categories