Concurrent processing in App Scripts
This demonstrates how to implement a function defined at global scope executed with more than one instance, concurrently in the background, and then processed when all instances have completed.
How
This technique uses two methods available in the Apps Scripts stack: UrlFetchApp.fetchAll
and the scripts.run
portion of the Apps Scripts API. The former interacts with the latter via its API conventions.
UrlFetchApp.fetchAll()
reaches out to external resources (including APIs) asyncronously. The requests that are passed through are not guaranteed to be executed in the order they appear. However, the responses do come back in the order they appear, since the function hands it back to us that way.
Since the App Script API (formally known as execution API) can be interacted with the same as an external resource, we can use that to call our own function. All we have to do is process the results.
Requirements
As this is an advanced topic, there are some special considerations to take into account, such as setup and limitations:
- You'll need to enable the Apps Scripts API on your project
- You'll need to Publish —> Deploy as executable API
- You'll need to edit your manifest to include one of the scopes listed in "Authorization Scopes" in the documentation i.e.
"oauthScopes": ["https://www.googleapis.com/auth/script.external_request"]
- You'll need to make sure doSomething function is available as a function on the project (cannot end with underscore)
Common pitfalls
If the script logs instructions to go to the Google Platform Project and turn on Apps Scripts API, copy the link and do that, as it is required to work.
If the script log outputs "Requested entity was not found" this could be either because you haven't defined scope, or because the apps scripts api was turned on recently and systems haven't propagated. If you have done all those, then I believe you have run into the same bug I have. (When I change devMode
to true it works.) Solution seems to edit the source, and save again.
Implications & Limitations
Subject to quota limitations, depending on the kind of domain in which the scripts are deployed. Please refer to Current quotas section for reference. Note that the quota counts for .fetchAll
is incremented according to how many resquests are passed to it. In other words, 10 requests passed to UrlFetchApp.fetchAll
counts for use of ten towards the quota.
Any project that uses this method will have to manage its manifest and scopes manually. This is because we have defined one scope manually, and thus the stack is no longer able to automanage it (by detecting what methods you are using and adding them automagically).
The Apps Scripts API at the time of writing was not compatible with the use of service accounts. This is not relevant to our example.
Example
Copy the code, run the myFunction
function. In the end it outputs the following:
[[row, of, students], [another row, of, students], [row, of, teachers], [another row, of, teachers], [row, of, parents], [another row, of, parents], [row, of, classes], [another row, of, classes]]
Since there are four doSomething
functions running asyncronously, it only takes 5 seconds, and not 15.