Skip to content

Snippets · 12 functions

Triggers and script control snippets

Each snippet is a complete function that installs a trigger, remembers a value between runs, or reports a failure, ready to paste into the Apps Script editor.

How to use these

Set up before you run

For onOpen and onEdit, open Extensions > Apps Script from a test spreadsheet. Save onOpen and reopen the spreadsheet to see its menu. Save onEdit and edit a cell below row 1 to supply its event. Do not run onEdit from the toolbar: a manual run has no edit event.

Before running a trigger installer, read its setup and make sure the named handler function exists in the same project. Run the installer, not the event handler, and remove your test triggers when you finish.

Copy a snippet into a script file, read its setup and notes, change the placeholder values, and save. For functions meant to run manually, choose the function name in the toolbar and press Run. Results appear in the Execution log unless the description says otherwise.

1

Create a daily time trigger if it does not exist

Checks the current user's installable triggers first, so running this function twice does not create a duplicate daily trigger for that user.

Code.gs
function createDailyTriggerIfMissing() {
  const handlerName = 'runDailyTask';
  const alreadyExists = ScriptApp.getProjectTriggers().some((trigger) => trigger.getHandlerFunction() === handlerName);
  if (alreadyExists) {
    Logger.log('Daily trigger already exists for the current user.');
    return;
  }
  ScriptApp.newTrigger(handlerName).timeBased().everyDays(1).atHour(6).create();
  Logger.log('Daily trigger created.');
}

function runDailyTask() {
  Logger.log('Daily task ran.');
}

Good to know. getProjectTriggers returns only the current user's installable triggers. Other editors must remove their own schedules.

2

Create a weekly trigger on Monday morning

Installs a trigger that runs an existing runWeeklyTask function once a week, on Monday, near 8 AM in the script's time zone.

Code.gs
function createWeeklyMondayTrigger() {
  const handlerName = 'runWeeklyTask';
  ScriptApp.newTrigger(handlerName)
    .timeBased()
    .onWeekDay(ScriptApp.WeekDay.MONDAY)
    .atHour(8)
    .create();
  Logger.log('Weekly Monday trigger created.');
}

Good to know. Before running this, add a runWeeklyTask function that performs your real weekly work. Apps Script calls the function by this exact name.

3

Create an hourly trigger

Installs a trigger that runs an existing checkInboxHourly function once every hour.

Code.gs
function createHourlyTrigger() {
  const handlerName = 'checkInboxHourly';
  ScriptApp.newTrigger(handlerName).timeBased().everyHours(1).create();
  Logger.log('Hourly trigger created.');
}

Good to know. Before running this, add a checkInboxHourly function that performs your real hourly work. Apps Script calls the function by this exact name.

4

Delete this user's triggers for one function

Removes the current user's installed triggers that call a particular function name, useful before that user reinstalls a trigger with new settings.

Code.gs
function deleteTriggersForFunction() {
  const handlerName = 'runDailyTask';
  const triggers = ScriptApp.getProjectTriggers().filter((trigger) => trigger.getHandlerFunction() === handlerName);
  triggers.forEach((trigger) => ScriptApp.deleteTrigger(trigger));
  Logger.log('Deleted ' + triggers.length + ' trigger(s) for the current user.');
}

Good to know. getProjectTriggers cannot delete schedules installed by another editor; each owner must remove their own triggers.

5

List this user's installed triggers with their handler names

Logs the function name and event type for every trigger the current user has installed on the project.

Code.gs
function logInstalledTriggers() {
  const triggers = ScriptApp.getProjectTriggers();
  triggers.forEach((trigger) => {
    Logger.log(trigger.getHandlerFunction() + ' - ' + trigger.getEventType());
  });
  Logger.log('Total triggers: ' + triggers.length);
}

Good to know. This does not include triggers that another editor installed; each owner sees their own installable triggers.

6

An onOpen that adds a custom menu

A simple trigger that runs automatically whenever its bound Google Sheet opens, adding a menu item.

Base.Ui
Code.gs
function onOpen() {
  SpreadsheetApp.getUi()
    .createMenu('Reports')
    .addItem('Send weekly summary', 'sendWeeklySummary')
    .addToUi();
}

function sendWeeklySummary() {
  Logger.log('Weekly summary sent.');
}

Good to know. This Sheets-only onOpen trigger cannot use services that need authorization, such as GmailApp, so keep it limited to building the menu.

7

An onEdit skeleton that ignores the header row

A simple trigger that runs on every edit in a bound Google Sheet, but exits immediately when the edited cell is in row 1.

Code.gs
/** @param {Events.SheetsOnEdit} e */
function onEdit(e) {
  const range = e.range;
  if (range.getRow() === 1) return;
  Logger.log('Edited cell: ' + range.getA1Notation());
}

Good to know. Exercise this by editing row 2 or below in the bound Sheet; running onEdit from the editor does not supply its event object.

8

Read and write a script property

Saves a value that survives between separate runs of the script, then reads it back.

Code.gs
function saveAndReadScriptProperty() {
  const properties = PropertiesService.getScriptProperties();
  properties.setProperty('LAST_RUN_STATUS', 'ok');
  const status = properties.getProperty('LAST_RUN_STATUS');
  Logger.log('Last run status: ' + status);
}

9

Use CacheService to remember a value for 10 minutes

Stores a value that expires on its own after 10 minutes, useful for avoiding repeat work without a manual cleanup step.

CacheService.Cache
Code.gs
function rememberValueForTenMinutes() {
  const cache = CacheService.getScriptCache();
  const tenMinutesInSeconds = 10 * 60;
  cache.put('LAST_API_RESULT', 'success', tenMinutesInSeconds);
  const cached = cache.get('LAST_API_RESULT');
  Logger.log('Cached value: ' + cached);
}

10

Use LockService to stop two runs from overlapping

Tries to get an exclusive lock before doing protected work, and skips the run if another copy of the script is already using it.

Code.gs
function runWithoutOverlap() {
  const lock = LockService.getScriptLock();
  const gotLock = lock.tryLock(10000);
  if (!gotLock) {
    Logger.log('Another run is already in progress. Skipping.');
    return;
  }
  try {
    Logger.log('Doing the protected work now.');
  } finally {
    lock.releaseLock();
  }
}

Good to know. tryLock waits up to the given number of milliseconds for the lock, then returns false if it still cannot get it.

11

A batch loop that stops after 4 minutes and saves where to resume

Processes items until it is close to the 6 minute script limit, saves its place, then picks back up on the next run.

Code.gs
function processItemsInBatches() {
  const properties = PropertiesService.getScriptProperties();
  const totalItems = 10000;
  const maxRunTimeMs = 4 * 60 * 1000;
  const startTime = Date.now();

  let index = Number(properties.getProperty('NEXT_INDEX') || '0');
  while (index < totalItems) {
    if (Date.now() - startTime > maxRunTimeMs) break;
    index++;
  }
  properties.setProperty('NEXT_INDEX', index >= totalItems ? '0' : String(index));
  Logger.log('Stopped at index ' + index + ' of ' + totalItems + '.');
}

Good to know. Pair this with a time trigger every few minutes so the next run continues from where the last one stopped.

12

Send yourself an email when a function throws

Wraps risky work in a try/catch block and emails you the error message, then rethrows it so the run is still recorded as failed.

GmailApp
Code.gs
function runProtectedTask() {
  try {
    riskyOperation();
  } catch (error) {
    const message = error instanceof Error ? error.message : String(error);
    GmailApp.sendEmail('you@example.com', 'Script failed', 'runProtectedTask failed: ' + message);
    Logger.log('Error emailed: ' + message);
    throw error;
  }
}

function riskyOperation() {
  throw new Error('Example failure for testing.');
}

Keep going

See these in a complete task

All snippet collections