Access all variables within a function even when error is thrown? - javascript

Below I have a main function. This function has many other variables declared in it and runs many functions. If an error occurs within the function I can catch this error but within my catch I do not have the value for the main function. I'm looking for a way to have access to all the variables even when an error is thrown.
export async function main ({alpha, beta, gamma}) {
let one = await doSomething(alpha)
let two = await doSomethingElse(one, beta)
return {one, two}
}
export async function store (db) {
await db.insert(data)
}
export async function usage (db, data) {
try {
let operation = await main(data)
await store (db, operation)
return operation
} catch (e) {
// here we don't have access to operation variables :(
await store(db, {}, e.message)
throw e
}
}
The only reasonable way i've found to do this is to create a class where each value in the main function.
import { get } from 'lodash'
export class Operation () {
constructor(db){
this.db = db
}
main({alpha, beta, gamma}) {
this.one = await doSomething(alpha)
this.two = await doSomethingElse(one, beta)
return {one: this.one, two: this.two}
}
init(data) {
try {
let main = await this.main(data)
await this.store(main)
return main
} catch (e) {
await this.store()
throw e
}
}
store(data = {}, errorMessage) {
if (!data.one) data.one = get(this, 'one') || null
if (!data.two) data.two = get(this, 'two') || null
if (errorMessage) data.errMessage = errorMessage
return await this.db.insert(data)
}
}
How can I have access to all the variables within a function even when an error is thrown?

The simple solution is to use var to declare a function-scoped variable instead of one that is scoped to the let block.
However, you really should just use two try/catch statements, for you are trying to catch two different errors and going to handle them differently:
try {
let operation = await main(data)
try {
await store (db, operation)
} catch(e) {
// here we do have access to the `operation` variable :)
}
return operation
} catch (e) { // failed to create operation (or the inner catch did rethrow)
await store(db, {}, e.message)
throw e
}

I created this class and wrapper function alwaysRunner. which takes two function arguments and will always provide the available variables to the secondary function.
export class AlwaysRunner {
constructor(operation, reguardless, optional = {}){
Object.assign(this, optional)
this.operation = operation.bind(this)
this.reguardless = reguardless.bind(this)
}
async init(data) {
let operation = this.operation
let reguardless = this.reguardless
let optional = this.optional
delete this.operation
delete this.reguardless
delete this.optional
try {
let main = await operation(data)
await reguardless(this)
return main
} catch (error) {
await reguardless({...this, error})
throw error
}
}
}
export async function alwaysRunner(operation, reguardless, optional) {
return await new AlwaysRunner(operation, reguardless, optional).init()
}
Here's some tests on how it works.
describe('alwaysRunner', () => {
it('should work', async () => {
let ran = [false, false]
await alwaysRunner(function () {
ran[0] = true
this.alpha = true
this.beta = true
return this
}, function (values) {
ran[1] = true
assert.equal(values.alpha, true)
assert.equal(values.beta, true)
assert.equal(values.error, undefined)
}).should.not.be.rejected
assert.deepEqual(ran, [true, true])
})
it('should work with thrown error', async () => {
let ran = [false, false]
await alwaysRunner(function () {
ran[0] = true
this.alpha = true
throw new Error('some error')
this.beta = true
}, function (values) {
ran[1] = true
assert.equal(values.alpha, true)
assert.equal(values.beta, undefined)
assert.equal(values.error, new Error('some error'))
}).should.be.rejected
assert.deepEqual(ran, [true, true])
})
})

Related

Catching and returning an error from a callback that may be asynchronous

I have a function like so:
const x = y(callback);
x();
It may be called with a synchronous or asynchronous callback:
const a = y(() => 42);
const b = y(async () => Promise.resolve(42));
The function y should take a callback, that can be synchronous or asynchronous. Is it possible to catch a thrown error from either a synchronous or asynchronous callback?
I can't simply wrap the callback in a try/catch:
const y = (callback) => function () {
try {
return callback(...arguments);
}
catch (callbackError) {
throw callbackError;
}
};
because that doesn't catch the errors thrown from promises. I can't just chain a .catch onto the callback as it might not be a promise.
I've read that checking if a function is a promise before calling it may not trivial/may not be possible.
I can't call it to check if it returns a promise before chaining the catch onto it because if it throws, the catch would not have yet been chained onto the call
Is there a way to catch an error from a callback that may or may not be a promise?
Per the comments, I should have specified I don't want the function to return a promise. That is:
const a = y(() => { throw new Error(); })();
console.log(a instanceof Error);
const b = y(async () => { throw new Error(); })();
console.log(b instanceof Error);
const c = y(() => 42)();
console.log(c === 42);
const d = y(async () => Promise.resolve(42))();
console.log(d instanceof Promise);
should all be true. Simply put, I want to return an error or the result
An async function also emits a promise, you might consider to also wrap the async inside a promise, which on resolution emits the value
Realised that it's not necessary to chain the catch immediately! So the following abomination allows me to determine if it is a promise and handle the error accordingly:
const y = (error, callback) => function () {
try {
const returnValue = callback(...arguments);
if (returnValue instanceof Promise) {
return returnValue.catch((callbackError) => {
return callbackError;
});
}
else {
return returnValue;
}
}
catch (callbackError) {
return callbackError;
}
};
If you're wondering why this function is useful, here's an example of what I'm using it for. It modifies the error (chaining it with another error, see VError) before returning it:
const y = (error, callback) => function () {
try {
const returnValue = callback(...arguments);
if (returnValue instanceof Promise) {
return returnValue.catch((callbackError) => {
throw VError(error, callbackError);
});
}
else {
return returnValue;
}
}
catch (callbackError) {
throw VError(error, callbackError);
}
};
Note the return of the following as per the question:
const a = y(() => 42)(); // returns '42'
const b = y(async () => Promise.resolve(42))(); // returns 'Promise {42}'
const c = y(() => { throw new Error('Base'); })(); // throws VError
const d = y(async () => { throw new Error('Base'); })(); // throws VError
You can use async/await notation here.
Await will work with both synchronous and asnychronous functions.
Try to do:
async function init() {
const x = y( async () => await callback() );
try {
await x();
}
catch(error) {
/.../
}
}
and your function can be simplified now to:
const y = (callback) => function (...argument) {
return callback(...arguments);
};
Well you can do something like this by making all your code async... If I get your problem right.
UPD: understand the problem, here is my approach
(async () => {
// Run fn
const fn = callback => {
return new Promise(async (resolve, reject) => {
const res = await callback().catch(err => reject(err));
// Resolve
resolve(res);
});
};
const variousFNs = [
// Function 1
{
fn: () => 2+2
},
// Function 2 with error
{
fn: () => {
const x = 1;
x = 2;
return x;
}
},
// Promise Function 3
{
fn: () => Promise.resolve(8)
},
// Promise function 4 with error
{
fn: () => new Promise(async (resolve, reject) => reject('some promise error'))
}
];
//
//
// Run all your fns
for (let i = 0; i < variousFNs.length; i++) {
try {
const res = await variousFNs[i].fn();
console.log(res);
} catch(err) {
console.log(`Error catched: ${err}`);
}
}
})();

Confusion around 'nested' try/catch statements in Javascript

Essentially I have an async function containing a try/catch that calls another async function also containing a try catch, and I'm getting a bit confused about how to properly implement what I'm doing. Some "pseudocode" showing my current implementation:
const main = async () => {
try {
const test = await secondFunc();
console.log(test);
} catch(err) {
console.log('Found an error!');
console.log(err);
}
const secondFunc = async () => {
try {
await performSomeRequestExample();
} catch(err) {
if (err.x === 'x') {
doSomething();
} else {
//********
throw err;
//********
}
}
So what I'm trying to do is get the throw(err) (surrounded by the asterisks) to be caught by the catch in main() which will also call the console.log('Found an error!'), but what currently happens is the error is thrown from secondFunc(), the catch in main() is never hit and I get an unhandled promise rejection.
Any guidance on what I'm doing wrong?
My advice is to minimize using try/catch unless absolutely necessary. With async functions (or any functions that return a Promise object) you can usually simplify things by not worrying about try/catch blocks unless you need to do something specific with certain errors. You can also use .catch rather than try/catch blocks to make things easier to read.
For example your code above could be written like this:
const main = async () => {
const test = await secondFunc().catch(err => {
console.log("Found an error from secondFunc!", err);
throw err; // if you want to send it along to main's caller
});
if (test) {
console.log("Test", test);
}
};
const secondFunc = () => {
return performSomeRequestExample().catch(err => {
if (err.x === "x") {
doSomething();
} else {
throw err;
}
});
};
const performSomeRequestExample = () => Promise.reject("bad");
main().then(
() => console.log("worked"),
err => console.log("failed from main", err)
);
In secondFunc we don't need to use async since we can just return the promise coming back from performSomeRequestExample and handle any failures in the .catch.
You should use
const secondFunc = async () => {
performSomeRequestExample().then(res =>{
console.log(res);
})
.catch(err => {
console.log(err);
}
)
Add a return before the await of performSomeRequestExample.
const secondFunc = async () => {
try {
return await performSomeRequestExample();
} catch (err) {
if (err.x === 'x') {
console.log('x');
} else {
throw err;
}
}
}
or you can also use .catch() after the awaited function.
Another solution can be like this
const main = async() => {
try {
const test = await secondFunc();
console.log(test);
} catch(err) {
console.log('Found an error!');
console.log(err);
}
}
const secondFunc = async () => {
//return await performSomeRequestExample(); //for success
return await performSomeRequestExample(2); //for error
}
const performSomeRequestExample = async(abc=1) => {
return new Promise(function(resolve,reject){
if(abc ==1){
setInterval(resolve("yes"),400);
}else{
setInterval(reject("opps"),400);
}
});
}
main();
Test this code at this link:
https://repl.it/repls/JoyfulSomberTelevision

How to handle 'auth/user-not-found' when i run my code?

I have an array of phone Number which is called phoneNumberArray and i tried to run this function when there is no user present with that number than this error comes i wanted to either reject that number and store in a variable
const user = async(acticityObject) => {
const phoneNumberArray = ['+91555555555','+915585565555'];
const getAuth = async phoneNumber => {
return await auth.getUserByPhoneNumber(phoneNumber).catch(console.log);
};
const userRecord = await Promise.all(phoneNumberArray.map(getAuth));
return;
}
module.exports = {user}
With await you can use ordinary try/catch:
const getAuth = async phoneNumber => {
try {
return await auth.getUserByPhoneNumber(phoneNumber);
} catch (err) {
// Handle, e.g.:
// console.log(err);
// if (err === 'auth/user-not-found') { ... }
}
};

"this" key word is undefined while calling static method inside another static method (JavaScript)

I'm exporting/importing an empty array to implement MVC patern and I was able to successfully store data in it by calling the allMentors method on the Mentor class.
Secondly, I want to be DRY and get the data from the array and then use it to find a specific mentor but I'm getting an empty array.
I searched the internet and I followed the example of using this keyword and call the static method inside another but NodeJS is throwing an error that this is undefined.
ALL MENTORS method
class Mentor{
static async allMentors(req, res) {
try {
users.forEach(user => {
if(user.is_mentor === true) {
mentors.push(user);
}
})
const ObjKeyRename = (src, map) => {
const dst = {};
for (const key in src) {
if (key in map)
// rename key
dst[map[key]] = src[key];
else
// same key
dst[key] = src[key];
}
return dst;
};
const uniqueMentors = Array.from(new Set(mentors.map(m => m.id)))
.map(id => {
return new Promise((resolve, reject)=> {
const currMentor = mentors.find(m => m.id === id);
const modMentor = ObjKeyRename(currMentor, { "id": "mentorId" });
return resolve(modMentor);
})
})
Promise.all(uniqueMentors).then(output => {
output.forEach(async obj => {
await delete obj['password'];
})
return res
.status(200)
.json(new ResponseHandler(200, 'All Mentors', output, null).result());
})
} catch (err) {
return res
.status(500)
.json(new ResponseHandler(500, err.message, null).result());
}
}
static async singleMentor(req, res) {
const returnedMentors = this.allMentors;
console.log('THE RETURNED:',returnedMentors)
const theMentor = returnedMentors.find(u => u.mentorId === parseInt(req.params.mentorId));
try {
if (!theMentor) return res
.status(404)
.json(new ResponseHandler(404, `Mentor number ${req.params.mentorId} not found`, null).result());
return res
.status(200)
.json(new ResponseHandler(200, 'Your mentor', theMentor, null).result());
} catch (err) {
return res
.status(500)
.json(new ResponseHandler(500, err.message, null).result())
}
}
}
export default Mentor;
What am I doing wrong? I appreciate your continued help as a JS learner.
There are multiple problems with your code:
a static method needs to be called by Class (Mentor) not by Reference (this)
a async method needs to be awaited or called with a callback (.then())
So for example you have the class MentorHandler:
class Mentor {
constructor() {
}
static async singleMentor(req, res) {
// ... some more code
}
static async allMentors(req, res) {
// await the result of the method
// call by class
const returnedMentors = await Mentor.allMentors();
// ... some more code
}
}

how to handle new Error() in node.js using ES6 Symbol?

I am creating a endpoint in node.js using ES6 Symbol. Example
// ES6 Symbol Method
const taskCreationMethod = {
[Symbol.taskMethod]() {
return {
storeCheckFunc: async function(storeId, employeeId) {
let store = await resourceModel["stores"].findById(storeId).populate(references["stores"]);
if(!store) {
return new Error("Store not found");
}
let employeeCheck = _.find(store.employees, (empObj) => {
return empObj._id == employeeId
})
if(!employeeCheck) {
return new Error("Employee not found");
}
return employeeCheck;
}
};
}
}
//end point
export const taskCreation = async(req, res) => {
const storeCheck = await taskCreationMethod[Symbol.taskMethod]().storeCheckFunc(req.body.store, req.body.assigned_to);
// here How can I handle return with Error Response?
}
You need to throw that error not just return it if you want to use the mechanisms of error handling. The thrown error will become a rejected promise which you can then handle with .catch() directly on the promise or with try/catch if you are using it in an async function. Here's a simplified example:
function populate() {
// always resolves to undefined
return Promise.resolve(undefined)
}
const taskCreationMethod = {
someMethod() {
return {
storeCheckFunc: async function() {
let store = await populate() // always resolves undefined
if (!store) { // so it always fails
throw new Error("Store not found"); // throw error
}
}
};
}
}
// regular promise then().catch()
taskCreationMethod.someMethod().storeCheckFunc()
.then(res => console.log(res))
.catch(err => console.log("Error:", err.message)) // catch
// OR … async function
async function runit() {
try {
let s = await taskCreationMethod.someMethod().storeCheckFunc()
} catch (err) {
console.log("Error:", err.message)
}
}
runit()

Categories