Scriptdrill

JavaScript for Sheets Updated November 2026

async, await and Promises in Apps Script

Apps Script runs everything synchronously: UrlFetchApp.fetch, SpreadsheetApp and GmailApp block until done, and there is no setTimeout. The V8 runtime parses async, await and Promise, so copied code compiles, but a Promise only resolves at the end of the run and gives no speed benefit. For parallel HTTP calls use UrlFetchApp.fetchAll; to wait use Utilities.sleep(ms).

Syntax

UrlFetchApp.fetchAll(requests) | Utilities.sleep(milliseconds)
PartWhat it is
fetchAll(requests)Array of {url, method, headers, payload, muteHttpExceptions}; responses return in the same order
Utilities.sleep(ms)Pause the script; counts toward the execution time limit

Examples

function getRate() {
  const res = UrlFetchApp.fetch("https://api.example.com/rate");
  return JSON.parse(res.getContentText()).rate;
}
Logger.log(getRate());

Result: The rate value, with no await needed because fetch already waited

const urls = ["https://api.example.com/a", "https://api.example.com/b", "https://api.example.com/c"];
const responses = UrlFetchApp.fetchAll(urls.map(u => ({url: u, muteHttpExceptions: true})));
const bodies = responses.map(r => JSON.parse(r.getContentText()));
Logger.log(bodies.length);

Result: 3, fetched in parallel instead of one after the other

function callWithRetry(url) {
  for (let attempt = 1; attempt <= 3; attempt++) {
    const res = UrlFetchApp.fetch(url, {muteHttpExceptions: true});
    if (res.getResponseCode() < 500) return res;
    Utilities.sleep(500 * attempt);
  }
  throw new Error("Server kept failing: " + url);
}

Result: Returns the first non-5xx response, waiting longer between attempts

async function demo() {
  const v = await Promise.resolve(1);
  Logger.log(v);
}
demo();
Logger.log("after");

Result: after is logged first, then 1 when the run drains the microtask queue; no parallel work happened

Common mistakes

  • setTimeout is not defined: there is no browser event loop; use Utilities.sleep for a pause or a time-driven trigger for later work.
  • A function returns a Promise instead of the value and the sheet shows [object Object]: drop async; Apps Script services return values directly.
  • Promise.all around UrlFetchApp.fetch is not faster: each fetch already blocked in order; use fetchAll for parallelism.
  • await in a custom function returns nothing useful: custom functions must return plain values synchronously.

Practice async, await and Promises in Apps Script

These drills check your answer on live data, free, no account needed.

  • Sleep with a PromiseReturn a Promise that resolves (with `undefined`) after `ms` milliseconds, so callers can write `await sleep(500)`. It must always return a Promise, even for `sleep(0)`. Tests run on a fake clock: `setTimeout`, `clearTimeout` and `Date.now` are simulated, so waits are instant and exact.
  • Retry a failing callCall `fn()` until it succeeds, at most `attempts` times, and resolve with its result. `fn` may return a Promise or a plain value, and it may throw or reject. If every attempt fails, reject with the error from the last attempt.
  • Promise.all with a concurrency limitLike `Promise.all(items.map(fn))`, but with at most `limit` calls of the async function `fn(item)` running at the same time. Resolve with the results in the order of `items`. Start the next item as soon as any running one finishes. If a call rejects, reject. Tests run on a fake clock: `setTimeout`, `clearTimeout` and `Date.now` are simulated, so waits are instant and exact.

Questions

Can I use async await in the HTML of a sidebar or web app?

Yes, the HTML runs in the browser with the normal event loop. Wrap google.script.run in a Promise there if you want await on the client side.

How do I run tasks in the background?

Create a time-driven or one-off trigger (ScriptApp.newTrigger(fn).timeBased().after(60000).create()) so the work happens in a separate execution.

Related

  • UrlFetchAppCall APIs from Google Apps Script with UrlFetchApp.fetch: GET and POST requests, headers and bearer tokens, JSON payloads, muteHttpExceptions, to a sheet.
  • try, catch and finallyHandle errors in Google Apps Script with try catch finally: catch exceptions from sheets, email and APIs, read err.message and err.stack, log and rethrow.
  • Time-driven triggersSchedule Google Apps Script with time-driven triggers: create them in the Triggers panel or with ScriptApp.newTrigger, run daily, hourly or weekly, delete.
  • Performance and batchingMake Google Apps Script faster: read and write in one getValues and setValues call, avoid getRange in loops, use flush and caching, beat the 6 minute limit.

Previous: JSON.parse and JSON.stringify