How do I organize my javascript code instead of nesting callbacks? - javascript

I'm making a application in javascript (Nodejs), I'm kinda new to it. My code needs to do multiple congruent requests, I organized my code in async functions so I can linearly call them
my first code looked like this
async function Fa(param,param1,callback,error){
//SOME CODE
}
async function Fb(param){
//SOME CODE
}
async function Fc(param){
//SOME CODE
}
function Fd(param,callback,error){
//SOME CODE
}
and use it like this
Fa(param,param1,
(result,result1) => {
Fb(resultB) => {
Fc(resultB);
}
},
(error) => { /*handle error*/ }
);
Fd(param,
(result)=>{
//handle result
},
(error)=>{
//handle error
}
)
of course this is not the right way to go for me...
so I got creative and wrote this
async function Fa(param,param1){
var errorFun,resultFun;
function setOnError(error){errorFun = error;}
function setOnResult(result){resultFun = result;}
async function execute(){
//SOME CODE HERE
}
return {setOnError,setOneResult,execute}
//I had to write a execute function because `Fa` being an async function I couldn't access setError and other inner functions from outside
}
I'm not repeating all the functions but I hope you got the idea
so my code looks like this
var resultA,resultA1;
var fa = await Fa(param,param1);
fa.setOnError((error) => /*handle error*/ );
//I want to terminate my code here (all this being in a function) but I don't know how to do so because I can't even set a flag to understand if error function has been called because I have multiple function with error and setting multiple flags would be stupid
fa.setOnResult( (result,result1) => {resultA = result; resultA1 = result1} );
await fa.execute()
var fb = await Fb(param);
fb.setOnResult((result) => {Fc(result);})
await fb.execute();
var fd = await Fd(param);
fd.setOnResult(/*some code*/);
fd.setOnError(/*some code*/);
await fd.execute();
I like my second version more but I don't know how to handle the errror (I want to stop executing the main function) and I think it's a bit overkill..
Any suggestion will be appreciated, thank you

you can try this code.
if execute function throw an error, it will be caught by the try-catch block in the main function
async function Fa(param, param1) {
var errorFun, resultFun;
function setOnError(error) { errorFun = error; }
function setOnResult(result) { resultFun = result; }
async function execute() {
//SOME CODE HERE
if (error) {
throw new Error(error);
}
}
return { setOnError, setOnResult, execute }
}
async function main() {
try {
var fa = await Fa(param, param1);
fa.setOnError((error) => /*handle error*/ );
fa.setOnResult((result, result1) => { resultA = result; resultA1 = result1 });
await fa.execute();
var fb = await Fb(param);
fb.setOnResult((result) => { Fc(result); });
await fb.execute();
var fd = await Fd(param);
fd.setOnResult(/*some code*/);
fd.setOnError(/*some code*/);
await fd.execute();
} catch (error) {
// handle error
}
}

Related

how to make an asynchronous javascript function synchronous

Given a button and it's click action set to be (in alpinejs)
<button x-on:click.prevent|debounce.3s="response = run();" type="button">Run</button>
when the run() function is defined like this:
function run() {
// captcha();
let data = input();
data = confirm(data);
...
data = request(url, data);
return data;
}
It is a synchronous function and it works nicely.
The recaptcha documentation (https://developers.google.com/recaptcha/docs/v3) states that if the button click is to be protected with recaptcha, it has to look like:
async function captcha() {
let resp;
grecaptcha.ready(function() {
grecaptcha.execute(recaptcha_site_key, {action: 'run'}).then(function(token) {
console.log(token);
resp = run();
});
});
return resp;
}
If the click action is set to be:
<button x-on:click.prevent|debounce.3s="response = await captcha();" type="button">Run</button>
It results to this error:
Can you please advise what I am doing wrong?
EDIT: added the return statement in the captcha function
for others that will be in the same situation as I am, here is the solution.
first step, I made the captcha function to return a promise
function captcha() {
return new Promise(function(resolve, reject) {
grecaptcha.ready(function() {
grecaptcha.execute(recaptcha_site_key, {action: 'run'}).then(function(token) {
resolve(token);
});
});
});
}
second step, async/await for the variable to become available
async function run() {
let g_recaptcha_response = await captcha();
document.forms['params'].elements['g_recaptcha_response'].value = g_recaptcha_response;
let data = input();
data = confirm(data);
if (data['error'] === true) {
return data;
}
let url = `${origin}/app/json`;
data = request(url, data);
// note: preparing for input / next request.
document.forms['params'].elements['token_id'].value = data['token_id'];
return data;
}
third step, create an IIFE at the top-level
(async function(){ let response = await run(); })();

JavaScript Try catch how to call same function in catch to retry

In JavaScript Try catch block how to retry the same . am facing once scenario any one ca give the best approach to handle this ?
for Example :
const getMyDetails = async()=>{
try{
await getName();
}catch(err){
//But Above FAIL due to some issue
// so I want to try the same again "getName" here until the success - 'not single time'
}
// Second Method for get data
const getName = async()=>{
try{
here am calling API or any kind of subscription But here its failing so am throwing error
}catch(err){
throw new Error(err);
}
getMyDetails()
Note: may be the fail reason like Databse events or some other subscriptions etc..
instead of calling the method name in catch .. what will be the best approach for retry
Thanks in advance
Just Calling Itself If It Fails Will Do The Job
const getMyDetails = async()=>{
try{
await getName();
}catch(err){
getMyDetails()
}
// Second Method for get data
const getName = async()=>{
try{
// Method
}catch(err){
throw new Error(err);
}
getMyDetails()
Here you can see an example behavior and the implementation logic of calling an async function N number of times, only until no errors are thrown.
// We must be inside of an async function to be able to call await
(async function() {
// Example async function which returns the value only if no errors are thrown,
// otherwise calls itself to retry the async call
async function getMyDetails() {
try {
return await getName();
} catch (error) {
return getMyDetails();
}
}
// Example async function which returns value only if the random number is even,
// otherwise throws an Error (simulating an API error response)
async function getName() {
return new Promise((resolve, reject) => {
let randomNumber = Math.floor(Math.random());
if (randomNumber % 2 === 0) {
resolve("Random Name");
}
reject("Example API Error has occured.")
});
}
let myDetails = await getMyDetails();
console.log(myDetails);
})();

Easiest way to wait for google server-side function to resolve

I need the client side code to wait for the called server side (google.script.run) function to complete before running any more code.
The withSuccessHandler(successFunc) does not cause lines of code that are after the server call to wait.
What I've done:
async function func(){
await google.script.run.withSuccessHandler(myFunc).serverFunc();
console.log("done");
}
func();
How can the code wait to execute the console.log line until after the server side function resolves?
How about this answer? Please think of this as just one of several answers.
Pattern 1:
In this pattern, after serverFunc was run, myFunc is run. At that time, console.log("done") is run in myFunc.
function myFunc() {
console.log("done");
}
function func(){
google.script.run.withSuccessHandler(myFunc).serverFunc();
}
func();
Pattern 2:
In this pattern, Promise was used. When you run func(), you can see ok and done in order.
function myFunc() {
return "ok";
}
async function func() {
await new Promise(r => {
google.script.run.withSuccessHandler(r).serverFunc();
});
const res = myFunc();
console.log(res);
console.log("done");
}
func();
Note:
If you test above samples, please set the function of serverFunc() at Google Apps Script side.
This is a simple sample script. So please modify this for your actual situation.
References:
Class google.script.run
withSuccessHandler(function)
Using Promises
If this was not the direction you want, I apologize.
Added:
If you want to use the values from serverFunc at myFunc, how about the following sample script?
Sample script:
function myFunc(nice) {
doStuffs(nice);
return "ok";
}
async function func() {
const e = await new Promise(r => {
google.script.run.withSuccessHandler(r).serverFunc();
});
const res = myFunc(e);
console.log(res);
console.log("done");
}
func();
In this script, the returned value from myFunc can be retrieved by res.
This code includes error handling and two Promise.then() methods.
The example given below is the complete code for an Apps Script Web App, that I used for testing purposes to make sure the that code works.
I needed a way to duplicate some client side code that uses fetch().then(), replacing the fetch(url) call to using Apps Script google.script.run.fncName().
H_Index
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<button onclick="some_Object_Name.innerNameOne()">Run</button>
<div id="idRslt1">Result One</div>
<div id="idRslt2">Result 2</div>
<div id="idError">For error</div>
<script>
function callServerAndGetRslt(po) {
/*
po.fncName - The name of the fnk to run
*/
//console.log('po.fncName ' + po.fncName)
return new Promise (function (resolve,reject) {
google.script.run
.withSuccessHandler (function (result) {
console.log('result 24' + result)
resolve (result);//What resolve does is return the result from the server back to the FIRST anonymous function in the "then" part
})
.withFailureHandler (function (error) {
console.log('error: ' + error)
reject (error);
})[po.fncName](po);//There can NOT be anything inbetween the array and the ending parenthesis
})
}
function showError(err) {
document.getElementById('idError').textContent = err;
}
function showResult(toShow) {
document.getElementById('idRslt1').textContent = toShow;
}
function showResult2(toShow) {
document.getElementById('idRslt2').textContent = toShow;
}
window.some_Object_Name = {
innerNameOne : function() {
return callServerAndGetRslt ({"fncName":'theFirstServerFncCall'})
.then (function (result) {
console.log('result: 45' + result)
showResult (result);
return callServerAndGetRslt ({"fncName":'serverFncCall2'});
},//THERE MUST BE A COMMA HERE!!!! This is a list of functions seperated by a comma
function (error) {//Because this is the second function this is what gets called for an error
showError(error);
return "There was an error in call 1";
}
).then (function (result) {
showResult2("end result:" + result);
});
}
}
</script>
</body>
</html>
GS_Test
function theFirstServerFncCall(po) {
Logger.log('po 1: ' + JSON.stringify(po))
//throw new Error("err in first fnk");//This is for testing the
//error handling on the client side
return ["name1","name2"];
}
function serverFncCall2(po) {
Logger.log('po 2: ' + JSON.stringify(po))
return [["one","two"]];
}
Code
function doGet() {
return HtmlService.createHtmlOutputFromFile("H_Index");
}
Thanks! This also solved my problem with lagged results from server for my dropdown values. Here is my code:
function createDropdowns() {
loaddropdown("Dropdowns!A2:A","ctype");
loaddropdown("Dropdowns!B2:B","state");
loaddropdown("Dropdowns!C2:C","city");
loaddropdown("Dropdowns!D2:D","remedies");
loaddropdown("Dropdowns!E2:E","keywords");
}
async function loaddropdown(range,eid) {
const e = await new Promise(r => {
google.script.run.withSuccessHandler(r).getDropdownList(range);
});
filldropdown(e,eid);
}
//POPULATE HTML DROPDOWN
function filldropdown(values, elemid) {
var list = document.getElementById(elemid);
for (var i = 0; i < values.length; i++) {
var option = document.createElement("option");
option.value = values[i];
option.text = values[i];
list.appendChild(option);
}
}

async/await not working and i dont see why

function projectHTML(e) {
let proj = await getProject(e.srcElement.dataset.id);
console.log(proj);
return `
<div id="Project">
<h1>${proj.name}</h1>
<p>Description: ${proj.description}</p>
</div>
`;
}
async function getProject(id) {
try {
let res = await fetch(`http://127.0.0.1:5000/api/projects/${id}`);
res = await res.json();
return res;
} catch (e) {
console.error(e);
}
}
The first function, projectHTML(), is being called from another file. I am able to return the value from function gethelp(), ProjectsHTML(). I can even log it in the console and indeed see it's correct. However, it won't load up into the template literal that I have. Please, someone lend a hand. I tried doing async...await in try blocks in ProjectsHTML() and still no luck.
Image of web issue
Function
async function getProject(id) {
try {
const res = await fetch(`http://127.0.0.1:5000/api/projects/${id}`);
return res.json();
} catch (e) {
console.error(e);
}
}
Call
getProject(id).then((data) => {
console.log(data)
})
You need to modify your code like following:
async function projectHTML(e) {
let proj = await getProject(e.srcElement.dataset.id);
console.log(proj)
return `<div id="Project">
<h1>${proj.name}</h1>
<p>Description: ${proj.description}</p>
</div>`;
};
If I am not wrong above function is an event handler and you are using passing it in your html. If not, and you are calling it from another function, you need to use awaitwhile calling this function:
await projectHtml(e)
Let me know if this helps.

Stuck in asnyc/await

Searched on many places and also went through many tutorials to deeply understand the async/awiat behavior of the javascript. Here is my code:
var bookAppointment = async (data) => {
return User.findOne({_id: data.user_id})
.then((userfound) =>{
//manipulate user's data and find if in array the specific object exist
var found = await userfound.dataArray.find( function(element){
return element.data == data.user_data
});
//here I want to wait until above result comes to evaulate below condition
if(found)
{
return "Sorry! data does not exist";
}
else
{
return userfound;
}
})
.catch(err => {
return err
});
}
What I want to achieve is to make my if else condition to wait above find function on array of javascript. Here is the error I'm facing:
SyntaxError: await is only valid in async function
I'm unable to understand where I'm wrong! Even my function has keyword async with its definition. Anyone around for a quick help?
At (userfound) =>{...} you're scoping another function.
For await to work, you need it to be something like async (userfound) => {...}
Catch and Then keyword are not used is async/await functions.
var bookAppointment = async (data) => {
var found = await User.findOne({_id: data.user_id});
try {
//do here like in then
} catch (e) {
//error handling
}
}

Categories