19

I'm a Java developer learning JavaScript and Google Apps Script simultaneously. Being the newbie I learned the syntax of JavaScript, not how it actually worked and I happily hacked away in Google Apps Script and wrote code sequentially and synchronous, just like Java. All my code resembles this: (grossly simplified to show what I mean)

function doStuff() {
  var url = 'https://myCompany/api/query?term<term&search';
  var json = getJsonFromAPI(url);
  Logger.log(json);
}

function getJsonFromAPI(url) {
  var response  = UrlFetchApp.fetch(url);
  var json = JSON.parse(response);
  return json;
}

And it works! It works just fine! If I didn't keep on studying JavaScript, I'd say it works like a clockwork. But JavaScript isn't a clockwork, it's gloriously asynchronous and from what I understand, this should not work at all, it would "compile", but logging the json variable should log undefined, but it logs the JSON with no problem.

NOTE:

The code is written and executed in the Google Sheet's script editor.

Why is this?

Gemtastic
  • 6,253
  • 6
  • 34
  • 53

3 Answers3

27

While Google Apps Script implements a subset of ECMAScript 5, there's nothing forcing it to be asynchronous.

While it is true that JavaScript's major power is its asynchronous nature, the Google developers appear to have given that up in favor of a simpler, more straightforward API.

UrlFetchApp methods are synchronous. They return an HttpResponse object, and they do not take a callback. That, apparently, is an API decision.

James Hearn
  • 61
  • 10
Madara's Ghost
  • 172,118
  • 50
  • 264
  • 308
  • But why does all my methods execute equentially as well? Shouldn't the logger log JSON before `getJsonFromAPI()` is finished executing? (it takes 20 seconds or so to run the `HttpFetchApp`. Aslo, GAS is supposed to be running the V8 engine, so why is it different from node? – Gemtastic Jul 06 '15 at 09:05
  • Your getJsonFromAPI() function doesn't do anything asynchronous. All of the function calls inside are synchronous calls. Therefore, it is also synchronous. – Madara's Ghost Jul 06 '15 at 10:37
  • I have other functions in my code that run in the same manner that is plain JS, and they run as intended too. But I gather then that google's entire GAS is synchronous? And thus my code will work fine as Java with JavaScript syntax? – Gemtastic Jul 06 '15 at 11:00
  • I haven't read the entire documentation for the GAS APIs, but generall, asynchronous functions either accept a callback, or return a Promise (or Future, as it's called in Java). If a function is neither of the above, it's *likely* to be synchronous. – Madara's Ghost Jul 06 '15 at 11:01
  • 1
    I've read a lot of the API but I can't find anything that tells me if it's synchronous or asynchronous, do you mind linking me to your source? – Gemtastic Jul 06 '15 at 11:05
  • 7
    no need to specify that. no api can be asynchronous if it doesnt return a promise or accept a callback, its that simple. – Zig Mandel Jul 06 '15 at 12:33
  • 2
    This also messed up my mind for a while. Now I understand – 0xh8h Aug 19 '19 at 04:55
16

Please note that this hasn't really changed since the introduction of V8 runtime for google app scripts.

While we are on the latest and greatest version of ECMAScript, running a Promise.all(func1, func2) I can see that the code in the second function is not executed until the first one is completed.

Also, there is still no setTimeout() global function to use in order to branch the order of execution. Nor do any of the APIs provide callback functions or promise-like results. Seems like the going philosophy in GAS is to make everything synchronous.

Steven de Salas
  • 20,944
  • 9
  • 74
  • 82
  • 6
    It's weird that they released v8, and still use this odd non-async approach to their ES6 implementation. This was their chance to achieve full parity with the real ES6. – DarkNeuron Feb 13 '20 at 11:23
5

I'm guessing from Google's point of view, that parallel processing two tasks (for example, that simply had Utilities.sleep(3000)) would require multiple threads to run in the server cpu, which may not be manageable and may be easy to abuse.

Whereas parallel processing on the client or other companies server (e.g., Node.js) is up to that developer or user. (If they don't scale well it's not Google's problem)

However there are some things that use parallelism


UrlFetchApp.fetchAll

UrlFetchApp.fetchAll() will asynchronously fetch many urls. Although this is not what you're truly looking for, fetching urls is a major reason to seek parallel processing.

I'm guessing Google is reasoning this is ok since fetchall is using a web client and its own resources are already protected by quota.


FirebaseApp getAllData

Firebase I have found is very fast compared to using a spreadsheet for data storage. You can get many things from the database at once using FirebaseApp's getAllData:

function myFunction() {
  var baseUrl = "https://samplechat.firebaseio-demo.com/";
  var secret = /* your secret */;
  var database = FirebaseApp.getDatabaseByUrl(baseUrl, secret);

  // paths of 3 different user profiles
  var path1 = "users/jack";
  var path2 = "users/bob";
  var path3 = "users/jeane";

  Logger.log(database.getAllData([path1, path2, path3]));
}

HtmlService - IFrame mode

HtmlService - IFrame mode allows full multi-tasking by going out to client script where promises are truly supported and making parallel calls back into the server. You can initiate this process from the server, but since all the parallel tasks' results are returned in the client, it's unclear how to get them back to the server. You could make another server call and send the results, but I'm thinking the goal would be to get them back to the script that called HtmlService in the first place, unless you go with a beginRequest and endRequest type architecture.


tanaikech/RunAll

This is a library for running the concurrent processing using only native Google Apps Script (GAS). This library claims full support via a RunAll.Do(workers) method.


I'll update my answer if I find any other tricks.

PoolloverNathan
  • 148
  • 2
  • 11
toddmo
  • 20,682
  • 14
  • 97
  • 107
  • 1
    You might find this [answer](https://stackoverflow.com/a/58070370/) useful. – TheMaster May 19 '20 at 04:34
  • I would like to disagree with your first paragraph since most modern JS runtimes have an event loop that adds (another level of) "fake multitasking". For example, `fetch()` sends the request, then *nothing **extra** runs* until the response comes back. – PoolloverNathan Apr 27 '23 at 12:34