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

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);
}
}

Related

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

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
}
}

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/jQuery, asynchronous call

Problem: how to wait with further code execution until the function returns some value?
Simple explanation:
I have an ajax request that returns some data, say animal groups: [Mammals, Birds, Reptiles], then in a loop I pass each of them as an argument to another function that do ajax request and returns for first element [Dog, Cat, Horse]... and then again ajax with [Dog] parameter that returns [Husky, Terrier, etc.].
The pseudocode looks something like:
function ask_breed(species){
var breed = $.getJSON('web');
var res;
breed.done(function (data) {
for (z in data){
res += z;
}
}
}
function ask_species(group){
var species = $.getJSON('web');
var res;
species.done(function (data) {
for (z in data){
res += z;
res += ask_breed(z);
}
}
}
function ask_group(){
var groups = $.getJSON('web');
var res;
groups.done(function (data) {
for (z in data){
res += z;
res += ask_species(z);
}
}
}
ask_group();
When it's failing:
When I update the result string, I'm getting 'undefined' instead of real data. The function is called correctly, but the result arrives with delay (tried with console.log).
I was trying to deal with it with 'done', and $.Deffered, and promises, but I just cannot make it work, it's not clear even after reading good dozen of articles about it.
Or maybe simpler version of problem:
function second_stage(param){
level2 = setTimeout(function(){console.log(param); return param;}, 3000);
return level2;
}
function initial(){
second_stage(1);
}
initial();
Try to take advantage of the new syntax sugar around Promises - async and await keywords.
Here is an example from the documentation
function resolveAfter2Seconds() {
return new Promise(resolve => {
setTimeout(() => {
resolve('resolved');
}, 2000);
});
}
async function asyncCall() {
console.log('calling');
var result = await resolveAfter2Seconds();
console.log(result);
// expected output: "resolved"
}
asyncCall();
function method1(){
//AJAX call
.done(function(data){
method2(data);
});
}
function method2(data2){
//AJAX call
.done(function(data2){
method3(data2);
});
}
Calling each method on success of each AJAX will do.

JavaScript - returning value from anonymous function parameter

I am using npm 'powershell' package for executing PowerShell commands and reading related output. I want to write a function that would return standard command output (so that I could call the the function and use its return value in assertions etc.).
const PowerShell = require("powershell");
var myFunction = function (command) {
let ps = new PowerShell(command);
ps.on("error", err => {
console.error(err);
});
ps.on("output", data => {
console.log(data);
//return data; <-- this does not work
});
ps.on("error-output", data => {
console.error(data);
});
ps.on("end", code => {
console.log("The end");
});
};
I want myFunction to return data value (from standard output). However, I don't know how to do it properly. Could you please advise?
Look into how callbacks work. An example for your function would be
var myFunction = function (command, callback) {
// code
ps.on("output", data => {
callback(data)
});
// code
};
myFunction('ls', function (data) {
console.log('The callback data:', data);
});

autobahn JS, what if RPC's callee is async?

In the documentation of autobahnJS is provided the following example to illustrate how to make setup a remote procedure call (RPC):
...
// 3) register a procedure for remoting
function add2(args) {
return args[0] + args[1];
}
session.register('com.myapp.add2', add2);
// 4) call a remote procedure
session.call('com.myapp.add2', [2, 3]).then(
function (res) {
console.log("Result:", res);
}
);
...
What if add2 needs to do some async operation? My idea was that maybe one can could call back another remote function registered in the client that triggered the initial call to backend.add2. Something like this:
...
//backend code
function add2(args) {
setTimeout(function() {
console.log("We are done here");
session.call('client.added', [123])
}, 1000);
return null; // useless, this value is never used
}
session.register('backend.add2', add2);
// client code
session.call('backend.add2', [2, 3]).then(
function (res) {
console.log("Result:", res);
}
);
...
Do you see any better option? This seems a bit cumbersome to me. Ideally add2 would return a promise. But I am not sure whether this is possible over a RPC?
You can return a promise which is then resolved once the async function returns.
From the AutobahnJS API reference page:
function myAsyncFunction(args, kwargs, details) {
var d = new autobahn.when.defer();
setTimeout(function() {
d.resolve("async finished");
}, 1000);
return d.promise;
}
My example of registering async function
session.register('com.forlunch.list_chats', function (args, kwargs, details) {
return functions.list_chats(args);
})
which make query to the mysql database
async function list_chats(params){
var query = "SELECT * WHERE ... ;"
let res = await mysql_query(query)
return res
}
function mysql_query (query){
return new Promise(function(resolve, reject) {
const con = mysql.createConnection(mysql_options);
con.query(query,[], function (err, rows, fields) {
if (err) return reject(err);
resolve(rows);
});
})
}

Categories