Skip to content

Instantly share code, notes, and snippets.

@brainysmurf
Last active November 5, 2020 07:38
Show Gist options
  • Save brainysmurf/afb107c7d0e3d8c1785b1320bdd469b4 to your computer and use it in GitHub Desktop.
Save brainysmurf/afb107c7d0e3d8c1785b1320bdd469b4 to your computer and use it in GitHub Desktop.
Concurrency (lack thereof) in Google Apps Scripts V8

Concurrency in Google AppsScripts (V8)

Steps to reproduce

  • Run useForLoop independently, and observe it takes about 7 seconds to complete
  • Run entrypoint which calls useForLoop three times, asynchronously
  • Observe that the second run takes at least three times as long as the first run to complete
  • Repeat the process above but in a browser, and observe the expected behaviour

Conclusion: Even though the documentation indicates that you can define async functions, and that you can make Promises, it ain't actually async.

Note

Even more proof is in the fact in observing what happens when you run in the browser. It executes entrypoint() immediately and "finishes" (although the calculations are still being made) and eventually outputs. But for the V8 engine, it indicates to the user that it is running and doesn't immediately return to the user.

Questions

  • Why is setTimeout undefined but Promises and AsyncFunction work as expected (minus the actual async)
  • Will Google have another implementation where they do work?

Related

Please see this gist to see the only way to do async on AppsScripts platform. (Note: It now requires you to create your own GCP project and link new project to that manually.)

/*
Wherein I set out to demonstrate that even though Google V8 engine syntax includes async and Promises
it is async in syntax only, and not in implementation.
The code below takes async function and runs them in parallel with `Promises.all`
But it is clear from the timing that it runs syncronously.
*/
/*
The promise returned in this function should resolve itself in 5 seconds, and running three of them
in parallel should also take 5 seconds.
Instead, an error is thrown as setTimeout is a `ReferenceError` and is undefined.
It is expected that on the first error thrown, it stops the other two (does not throw three errors).
(That setTimeout is a ReferenceError probably be the only clue we need that the stack is not async.)
*/
function useSetTimeout () {
const fiveSeconds = 5000;
return new Promise(resolve => setTimeout(resolve, fiveSeconds)); // ReferenceError: setTimeout not defined
}
async function calc(i, j) {
return i + j;
}
/*
The promise returned in this function should resolve itself in about 7 seconds, and running three of them
in parallel should also take about 7 seconds.
Instead, it takes 24 seconds (your mileage may vary) ... as if it had to run three times syncronously
(This indicates that it isn't being run in parallel like it should be.)
*/
function useForLoop () {
return new Promise( resolve => {
let total = 0;
for (let i = 0; i <= 10000; i++) {
for (let j = i; j > 0; j--) {
total += await calc(i + j);
}
}
resolve("completed");
} );
}
/*
This function does the work of attempting to run the async functions in paraellel.
*/
async function concurrency(func) {
let promises = [];
promises.push(func());
promises.push(func());
promises.push(func());
// Send an array of promises which should be executed
let results = await Promise.all(promises);
return results;
}
/*
This function implements timing, calls `concurrency`, which hands off to the intended asyncFunction
*/
function timeit (asyncFunction) {
const start = new Date().getTime();
concurrency(asyncFunction).then(result => {
const end = new Date().getTime();
const duration = (end - start) / 1000;
Logger.log(`function ${asyncFunction.name} took ${duration} seconds to complete`);
}).catch(error => {
Logger.log(error);
});
}
/*
Execute this function from the play button in the online editor
*/
function entrypoint () {
timeit(useSetTimeout);
timeit(useForLoop);
}
@brainysmurf
Copy link
Author

Not sure why you're testing client-side.
In any case, how does useForLoop get called on the client-side?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment