Catch block wrapping a function that throws inside a setTimeout not executed - javascript

Experienced something strange recently, none of the catch blocks get executed:
function funcWillThrow() {
try {
setTimeout(() => {
throw "Has thrown";
}, 3000);
} catch (error) {
console.log(error)
}
}
function caller() {
funcWillThrow();
}
caller();
and
function funcWillThrow() {
setTimeout(() => {
throw "Has thrown";
}, 3000);
}
function caller() {
funcWillThrow();
}
try {
caller();
} catch (error) {
console.log(error);
}
And according to the mdn docs
Execution of the current function will stop (the statements after
throw won't be executed), and control will be passed to the first
catch block in the call stack. If no catch block exists among caller
functions, the program will terminate.
My guess is that something is happening with the callstack as stated in the docs. Any ideas as to what might be happening.

setTimeout calls the supplied function at a later time (3 seconds later in your specific code), and by then the function which called setTimeout has long since terminated.
To catch the exception in your later function, put the error handling in that function:
function funcWillThrow() {
setTimeout(() => {
try {
throw "Has thrown";
} catch (error) {
console.log(error);
}
}, 3000);
}
Based on your comment below, you may be looking for Promises. Since the operation is asynchronous and happens outside of the stack which invoked it, in order to catch the error in that stack you'd need to await the operation. Ideally you'd make use of the reject functionality of the Promise. Something like this:
function funcWillThrow() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject("Has thrown");
}, 3000);
});
}
(async () => {
try {
await funcWillThrow();
} catch (err) {
console.log(err);
}
})();
If you specifically need to be able to throw from within the setTimeout callback, you'd either need to catch there as well:
function funcWillThrow() {
return new Promise((resolve, reject) => {
setTimeout(() => {
try {
throw "Has thrown";
} catch (error) {
reject(error);
}
}, 3000);
});
}
(async () => {
try {
await funcWillThrow();
} catch (err) {
console.log(err);
}
})();
Or customize setTimeout itself as a Promise and use normal error handling with that:
function myTimeout(ms) {
return new Promise(function(resolve) {
setTimeout(resolve, ms);
});
}
(async () => {
try {
await myTimeout(3000).then(() => {
throw "Has thrown";
});
} catch (err) {
console.log(err);
}
})();

Related

can't get an expect console output when using async await

function fails4() {
return new Promise((resolve, reject) => {
setTimeout(function () {
reject(new Error());
}, 100);
});
}
async function myFunc4() {
try {
await fails4();
} catch (e) {
console.log(e);
console.log('that failed', e); //<-- this gets called
}
}
async function loadmYScript() {
try {
await myFunc4();
} catch (error) {
console.log(error);
console.log(123);
}
}
loadmYScript();
cant't execute the console.log(123) as I expected can anybody help me with this question very appreciated
You're calling loadmYScript, which in turn calls myFunc4, which in turn calls fails4. This last one (fails4) throws an error. The error is "catch-ed" by myFunc4. Inside this catch block you don't throw any error, there's only a couple of logs, so the result of loadmYScript is a fulfilled promise with undefined value. It is not rejected because myFunc4 doesn't throw the error.
If you throw an error inside the catch block of myFunc4, you will have your 123 logged, and the promise will be rejected.

Does a return statement in the try block wait for an await statement in the finally block?

Consider the following snippet:
try {
const client = getClientThatMustBeClosed();
const data = await shortRunningOperation(client);
return Promise.resolve(data);
} catch (error) {
return Promise.reject(error);
} finally {
// any exception in the following function is caught and dealt with internally...
await longRunningOperation(client);
client.close();
}
Ideally I want to return data to the calling function right after shortRunningOperation() completes, without waiting for longRunningOperation() to complete. But closing the client must wait until after longRunningOperation() completes.
This jsfiddle would suggest that the return statement waits for the finally block to complete... in which case, what is the correct way to get data back to the calling function ASAP without waiting for longRunningOperation() to complete?
Here's some simple test code that shows that the finally block does execute, even after the try block returns.
function wait (ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
})
}
async function test () {
try {
await wait(1000);
console.log('try block complete');
return;
} catch (err) {
console.log('catch err:', err);
return;
} finally {
await wait(3000);
console.log('finally block complete');
}
}
test();
But as the OP notes, the try block value will not be returned until the finally block is completed.
If the try return value is wanted immediately, don't use a finally block. Instead, put the long duration cleanup code in the try block, but don't use the await.
function wait (ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
})
}
async function test () {
try {
await wait(1000);
console.log('try complete');
// put cleanup here without using await
wait(3000).then(() => {
console.log('cleanup complete');
});
return 'try return';
} catch (err) {
console.log('catch err:', err);
return err;
}
}
test().then(result => console.log('result:', result));
Update: You can use a finally block without blocking (delaying) the try return, but only if the finally block does not contain a return or await.
function wait(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
async function test() {
try {
await wait(1000);
console.log('try complete');
return 'try return';
} catch (err) {
console.log('catch err:', err);
return err;
} finally {
// cleanup without await
wait(3000).then(() => {
console.log('long duration cleanup complete');
});
// do not return here
}
}
test().then((result) => console.log('result:', result));

"await is only valid in async function" in for loop

I'm being told that "await is only valid in async function", even though it is in a async function. Here is my code:
async function uploadMultipleFiles (storageFilePaths,packFilePaths,packRoot) {
return new Promise((resolve,reject) => {
try {
for (i in storageFilePaths) {
await uploadFile(storageFilePaths[i],packFilePaths[i],packRoot) // error throws on this line
}
resolve("files uploaded")
} catch {
console.log(err)
reject("fail")
}
})
}
Why is this happening when I made it an async function? Is it because I am using a for loop? If so, how can I get the expected outcome without this error?
The function you define starting on line 1 is async.
The arrow function you define on line 2 and pass to the Promise constructor is not async.
You are also using the multiple promise anti-pattern. Get rid of the Promise constructor entirely. Just return the value when you have it. That's one of the main benefits of the async keyword.
async function uploadMultipleFiles(storageFilePaths, packFilePaths, packRoot) {
try {
for (i in storageFilePaths) {
await uploadFile(storageFilePaths[i], packFilePaths[i], packRoot) // error throws on this line
}
return "files uploaded";
} catch {
console.log(err);
throw "fail";
}
}
You can only use await inside of an async function, the error refers to the callback your passing to your new Promise (since you are entering a new function scope there).
async function uploadMultipleFiles (storageFilePaths,packFilePaths,packRoot) {
return new Promise((resolve,reject) => { // <========= this arrow function is not async
try { // so you cant use await inside
for (i in storageFilePaths) {
await uploadFile(storageFilePaths[i],packFilePaths[i],packRoot) // error throws on this line
}
resolve("files uploaded")
} catch {
console.log(err)
reject("fail")
}
})
}
The part where you try to construct a new Promise is actually redundant since an async function will resolve to a Promise anyways (read more here). So you could write your code as follows:
async function uploadMultipleFiles (storageFilePaths,packFilePaths,packRoot) {
try {
for (i in storageFilePaths) {
await uploadFile(storageFilePaths[i],packFilePaths[i],packRoot) // error throws on this line
}
return "files uploaded"
} catch {
console.log(err)
throw new Error("fail");
}
}
The Promise callback isn't async
async function uploadMultipleFiles (storageFilePaths,packFilePaths,packRoot) {
return new Promise(async (resolve,reject) => {
try {
for (i in storageFilePaths) {
await uploadFile(storageFilePaths[i],packFilePaths[i],packRoot) // error throws on this line
}
resolve("files uploaded")
} catch {
console.log(err)
reject("fail")
}
})
}

Jest test callback promise

I want to test the following storeCache function.
The critical part I want to test is the callback function in Meteor.call(...).
I want to mock Meteor.call(...) but it is wrapped by a Promise and the callback itself also relies on the wrapping Promise.
export async function storeCache(cache) {
// do something
return new Promise((resolve, reject) => {
Meteor.call("transferCache", cache, async (error, result) => {
if (error) {
reject(error);
} else {
try {
const result = await persistCache();
resolve(result)
} catch (e) {
reject(e);
}
}
});
});
}
What is the best way to test the defined callback function via Jest?
Or is there a better way to structure the Code to make it easiert to test?
it should be as simple as
// in your imports
import { Meteor } from 'meteor/meteor'
jest.mock('meteor/meteor', () => {
return {
Meteor: {
call: jest.fn()
}
}
})
//in your test case
Meteor.call.mockImplementation((eventName, cache, callback) => {
// your assertions
// you can call the callback here
// you probably want to mock persistCache too
})
So here's my solution:
The Meteor.call with its promise is in a separate function called helper.transferData().
This removed the necessity for a callback. I can put the logic directly in
storeCache() and mock helper.transferData().
export async function transferData(cache) {
return new Promise((resolve, reject) => {
Meteor.call("transferCache", cache, async (error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
}
export async function storeCache(cache) {
// do something
try{
// call meteor method
const transferResult = await helper.transferData(cache);
// callback logic
const result = await persistCache();
// return Promise for compability reasons
return Promise.Resolve(result);
} catch (e) {
return Promise.Reject(result);
}

Handling Js promise rejection

How do you handle an error (eg. "new error" below) that is outside of the promise?
function testError() {
throw new Error("new error") // how to handle this?
var p123 = new Promise(function(resolve, reject) {
resolve(123)
});
return p123
};
testError().catch(err => {
return err; // code doesn't come here
})
.then(ok => {
console.log(ok)
});
If you're not sure whether a function will throw (or return a value) synchronously, you can call it using .then(). This will wrap it so that the result will be handled consistently no matter how it is produced:
function testError() {
throw new Error("new error") // how to handle this?
var p123 = new Promise(function(resolve, reject) {
resolve(123)
});
return p123
};
Promise.resolve()
.then(testError)
.catch(err => {
console.error(err);
return err;
})
.then(ok => {
console.log(ok.message)
});
Since the error doesn't involve the async code, a regular try-catch should do fine here:
try {
testError().catch(err => {
return err; // code doesn't come here
})
.then(ok => {
console.log(ok)
});
}
catch(e) {
//
}
Note that when the async-await pattern finally becomes the native way of resolving promises, the try-catch will also become the native way of handling errors:
try {
var ok = await testError();
console.log(ok)
}
catch(e) {
console.log('e:' +e);
}
As one can easily verify, this one correctly handles both the sync and the async error and is much cleaner than then-catch.
If you can, rewrite your testError function like so
function testError () {
return new Promise(function (resolve, reject) {
throw new Error('new error')
resolve(123)
})
}
testError().then(ok => console.log(ok),
err => console.error(err.message))
Run it once to see it throw the error in console.error
Comment out the throw line to see the promise resolve successfully
Since the error is thrown outside of the promises, you cannot catch it using a promise catch statement.
You can use a try/catch to catch the error instead.
function testError() {
throw new Error("new error") // how to handle this?
var p123 = new Promise(function(resolve, reject) {
resolve(123)
});
return p123
};
try {
testError().then(ok => {
console.log(ok)
});
} catch (err) {
console.log(err.message);
}
You rewrite it, because making a caller check for both exceptions and rejections is an anti-pattern:
function testError() {
return Promise.resolve().then(() => {
throw new Error("new error"); // rejects returned promise
return new Promise(function(resolve) {
resolve(123);
});
});
}
testError().catch(err => console.log("Caught " + err));
This is implicit with async functions; they always return a promise:
async function testError() {
throw new Error("new error"); // rejects implicit promise
return await new Promise(function(resolve) {
resolve(123);
});
}
testError().catch(err => console.log("Caught " + err));

Categories

Resources