How to combine 2 Javascript functions into one? - javascript

I have the following function, which I want to re-use as an "action" template and pass another function as a parameter, which will be the action function, to be executed in the middle of it.
QUESTION
Is it possible? How can I do it?
Note that the "action" is asynchronous and also I'm using React.
function templateAction(action) {
try {
setLoading(true);
setError(null);
// DO SOMETHING
action();
setLoading(false);
}
catch(err) {
console.log(err);
setError(err);
setLoading(false);
}
}
And inside that action() call should execute the following function:
async function getBlogPost() {
const querySnapshot = await firebase.firestore().collection('blog').where('slug','==',props.match.params.slug).get();
console.log(querySnapshot.docs);
if (querySnapshot.docs.length === 0) {
throw 'ERROR: BlogPost not found...';
} else if (querySnapshot.docs.length > 1) {
throw 'ERROR: More than 1 blogPost found...';
}
const blogPostData = querySnapshot.docs[0].data();
setFirestoreID(querySnapshot.docs[0].id);
setBlogPost(blogPostData);
}
SNIPPET
I think that I've built the behavior that I need (run snippet). Can I do it simpler than that? It seems a lot of boilerplate.
async function templateAction(action) {
try {
console.log('Template Action BEFORE calling action');
await action();
console.log('Template Action AFTER calling action');
}
catch(err) {
console.log(err);
}
}
function action() {
return new Promise(async (resolve,reject) => {
console.log('I am sync from action function BEFORE async call');
await mockAPI();
console.log('I am sync from action function AFTER async call');
resolve();
});
}
function mockAPI() {
return new Promise((resolve,reject) => {
setTimeout(() => {
console.log('I am from async mockAPI call');
resolve();
},1500);
});
}
templateAction(action);

You can pass by anonymous function like this: https://codesandbox.io/embed/awesome-mayer-52ix7
function templateAction(action) {
action();
}
let test = function() {
alert("test");
};
templateAction(test);
For the async part, maybe you will need to make promise, depending if you need a confirmation for the action.
With reactJS you can make this more simple by using scope sending properties and getting using "this.props"

Here is my final code, with error catching using try catch blocks.
It works, though I think it hurts readability.
async function templateAction(action) {
try {
console.log('Template Action BEFORE calling action');
await action();
console.log('Template Action AFTER calling action');
}
catch(err) {
console.log('I was caught and logged');
console.log(err);
}
}
function action() {
return new Promise(async (resolve,reject) => {
try {
console.log('I am sync from action function BEFORE async call');
await mockAPI();
console.log('I am sync from action function AFTER async call');
resolve();
}
catch(err) {
console.log('I was caught');
reject(err);
}
});
}
function mockAPI() {
return new Promise((resolve,reject) => {
setTimeout(() => {
console.log('I am from async mockAPI call');
resolve();
},1500);
});
}
templateAction(action);

Related

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?

Can't change code from .then to async/await

trying to change .then, like this:
User.prototype.login = () => {
return new Promise((resolve, reject) => {
this.cleanup();
usersCollection
.findOne({ username: this.data.username })
.then((attemptedUser) => {
if (attemptedUser && attemptedUser.password == this.data.password) {
resolve("logged in");
} else {
reject("invalid something");
}
})
.catch(() => {
reject("Please, try again later");
});
});
First one works perfectly, but when I try to change it to async/await, like this:
User.prototype.login = () => {
return new Promise(async (resolve, reject) => {
this.cleanup();
try {
const attemptedUser = await usersCollection.findOne({ username: this.data.username });
if (attemptedUser && attemptedUser.password == this.data.password) {
resolve("logged in");
} else {
reject("invalid something");
}
} catch {
reject("Please, try again later");
}
});
};
it gives me an error that this.cleanup() is not a function, and after a few tries, I realized that async somehow change "this".
can you please help me, where did I made an error?
2 problems in your code:
Functions defined on a prototype object should be regular functions instead of arrow functions because functions defined on objects are used as methods, i.e. value of this inside them refers to instances of the constructor function or class. Using an arrow function will set incorrect value of this and that is most likely the cause of error in your code.
You have mixed promise-chaining with async-await syntax.async functions always return a Promise, so instead of explicitly returning a Promise object, make login function an async function and return the string from the function which is same as calling resolve() function with the string. To reject a promise, throw an error from an async function.
This is how you should define login function
User.prototype.login = async function() {
this.cleanup();
try {
const attemptedUser = await usersCollection.findOne({username: this.data.username});
if (attemptedUser && attemptedUser.password == this.data.password) {
return "logged in";
}
throw new Error("invalid something");
} catch (error) {
console.log("Please, try again later");
throw error;
}
};

Break out of Async Function Within a Catch Block

I have the following function:
async myFunction() {
await this.somePromiseFunction().then(
() => alert('Promise Complete!'),
() => {
throw new Error('Error');
}
).catch(() => {
alert('End the Function Here');
});
alert('Do not get called if error caught above.');
await this.anotherPromiseFunction().then(
() => alert('Promise Complete!'),
() => {
throw new Error('Error');
}
).catch(() => {
alert('End the Function Here');
});
}
I would like it such that when an error is caught in the promise return handler that it ends the asynchronous function as I do not want it to continue in that case.
Instead of mixing await with .then(), just await each asynchronous function call directly in a try block and deal with the error appropriately.
If the asynchronous function returns a rejected promise, await will cause the rejection to throw from the try block and be caught, skipping the rest of the control flow inside the try.
const asyncFactory = label => async () => {
await new Promise(resolve => { setTimeout(resolve, 1000); });
if (Math.random() < 0.25) {
throw new Error(`${label} Error`);
}
console.log(`${label} Complete!`);
};
const somePromiseFunction = asyncFactory('somePromiseFunction');
const anotherPromiseFunction = asyncFactory('anotherPromiseFunction');
async function myFunction() {
try {
console.log('Start myFunction here');
await somePromiseFunction();
await anotherPromiseFunction();
} catch (error) {
console.log('Error caught:', error.message);
} finally {
console.log('End myFunction here');
}
}
myFunction();
You can actually achieve the equivalent without using async and await, and you don't need to nest your promises to do so:
const asyncFactory = label => () => {
return new Promise(resolve => {
setTimeout(resolve, 1000);
}).then(() => {
if (Math.random() < 0.25) {
throw new Error(`${label} Error`);
}
console.log(`${label} Complete!`);
});
};
const somePromiseFunction = asyncFactory('somePromiseFunction');
const anotherPromiseFunction = asyncFactory('anotherPromiseFunction');
const oneMorePromiseFunction = asyncFactory('oneMorePromiseFunction');
function myFunction() {
console.log('Start myFunction here');
return somePromiseFunction().then(() => {
return anotherPromiseFunction();
}).then(() => {
return oneMorePromiseFunction();
}).catch(error => {
console.log('Error caught:', error.message);
}).finally(() => {
console.log('End myFunction here');
});
}
myFunction();
Do note that Promise.prototype.finally() is actually part of ECMAScript 2018, so if the browser supports it natively, it will also already support async and await. However, it can be polyfilled while async and await cannot.

js - How to call an async function within a Promise .then()

First, I have to mention that I already look through many questions in stackoverflow, but many doesn't answer my question. Not to mention many doesn't even have an answer.
How do I achieve the following, making sure functionB() executes after functionA() finishes?
Note: I do not want to convert my async functions to new Promise(resolve=>{...})
because I'll have to convert the someServiceThatMakesHTTPCall() as well, and any other async functions within the call stack, which is a big change.
function functionThatCannotHaveAsyncKeyword() {
functionA()
.then(async function() {
await functionB();
})
.then(function() {
console.log('last');
});
}
async function functionA() {
console.log('first');
await someServiceThatMakesHTTPCall();
}
async function functionB() {
console.log('second');
await someServiceThatMakesHTTPCall();
}
Your approach using await in an async then callback will work, but it's unnecessarily complex if all you want to do is call the async function and have its result propagate through the chain. But if you are doing other things and want the syntax benefit of async functions, that's fine. I'll come back to that in a moment.
async functions returns promises, so you just return the result of calling your function:
function functionThatCannotHaveAsyncKeyword() {
functionA()
.then(function() {
return functionB(someArgument);
})
.then(function() {
console.log('last');
}); // <=== Note: You need a `catch` here, or this function needs
// to return the promise chain to its caller so its caller can
// handle errors
}
If you want to pass functionA's resolution value into functionB, you can do it even more directly:
functionA()
.then(functionB)
// ...
When you return a promise from a then callback, the promise created by the call to then is resolved to the promise you return: it will wait for that other promise to settle, then settle the same way.
Example:
const wait = (duration, ...args) => new Promise(resolve => {
setTimeout(resolve, duration, ...args);
});
async function functionA() {
await wait(500);
return 42;
}
async function functionB() {
await wait(200);
return "answer";
}
functionB()
.then(result => {
console.log(result); // "answer"
return functionA();
})
.then(result => {
console.log(result); // 42
})
.catch(error => {
// ...handle error...
});
Coming back to your approach using an async then callback: That works too, and makes sense when you're doing more stuff:
const wait = (duration, ...args) => new Promise(resolve => {
setTimeout(resolve, duration, ...args);
});
async function functionA() {
await wait(500);
return 42;
}
async function functionB() {
await wait(200);
return "answer";
}
functionB()
.then(async (result) => {
console.log(result); // "answer"
const v = await functionA();
if (v < 60) {
console.log("Waiting 400ms...");
await wait(400);
console.log("Done waiting");
}
console.log(v); // 42
})
.catch(error => {
// ...handle error...
});
You can use promise inside the first method as
function functionThatCannotHaveAsyncKeyword() {
return new Promise(async(resolve, reject)=> {
await functionA();
await functionB();
console.log('last');
resolve();
});
}
async function functionA() {
console.log('first');
await someServiceThatMakesHTTPCall();
}
async function functionB() {
console.log('second');
await someServiceThatMakesHTTPCall();
}
if someServiceThatMakesHTTPCall is async you can avoid all that by doing the following:
function functionThatCannotHaveAsyncKeyword() {
functionA()
.then(function() {
return functionB()
})
.then(function() {
console.log('last');
});
}
function functionA() {
console.log('first');
return someServiceThatMakesHTTPCall();
}
function functionB() {
console.log('second');
return someServiceThatMakesHTTPCall();
}

How to resolve promises and catch an error

I am trying to user webgazer.js where my code basically checks to see whether the webgazer is initialized and when it is initialized it resolves a promise which dispatches an action. This works however if for example there is no webcam I need to throw an error. The error in my code never gets called.
Here is my code
export function detectJsCamera() {
return async(dispatch) => {
dispatch({type: types.JS_DETECTING_CAMERA});
try {
await detectCamera();
await dispatch({type: types.JS_CAMERA_DETECTED});
} catch (error) {
await dispatch({type: types.CAMERA_DETECTION_FAILED, error: error.message});
throw error;
// this.props.history.push('/setup/positioning')
};
}
}
const detectCamera = () => new Promise((resolve, reject) => {
const checkIfReady = () => {
if (webgazer.isReady()) {
resolve('success');
} else {
console.log('called')
setTimeout(checkIfReady, 100);
}
}
setTimeout(checkIfReady,100);
});
You will need to reject in order to throw an exception like below
const detectCamera = () => new Promise((resolve, reject) => {
const checkIfReady = () => {
if (webgazer.isReady()) {
resolve('success');
} else {
console.log('called');
reject("some error");
}
}
setTimeout(checkIfReady,100);
});
You need to call reject() in your detectCamera method when your webgazer is not initialised then it would be caught in your catch block in detectJsCamera method.

Categories