Naar inhoud
lightbulb Welkom op de nieuwe kennisbank | We hebben de docs volledig vernieuwd met meer dan 160 features. Bekijk wat nieuw isarrow_forward

Performance optimaliseren in Apps Script

Maak Apps Script-scripts sneller met batch-operaties, caching, het vermijden van service-aanroepen in lussen en slimme API-strategieen.

De root cause van Apps Script-traagheid

Apps Script is traag wanneer het te veel aanroepen doet naar Google-services zoals Sheets, Drive en Gmail. Elke aanroep naar getValue(), getRange() of sendEmail() communiceert via de server. In een lus van 1000 iteraties zijn dat 1000 serverrondritten.

De oplossing is simpel: batch alles. Lees eenmaal, verwerk in JavaScript, schrijf eenmaal.

Batch lezen met getValues()

function traag() {
  const blad = SpreadsheetApp.getActiveSheet();
  const aantalRijen = blad.getLastRow();

  let totaal = 0;
  for (let i = 1; i <= aantalRijen; i++) {
    totaal += blad.getRange(i, 1).getValue();
  }
  Logger.log(totaal);
}

function snel() {
  const blad = SpreadsheetApp.getActiveSheet();
  const waarden = blad.getRange(1, 1, blad.getLastRow(), 1).getValues();

  const totaal = waarden.reduce((som, rij) => som + Number(rij[0] || 0), 0);
  Logger.log(totaal);
}

Het verschil: traag() doet N serveraanroepen, snel() doet er 1.

Batch schrijven met setValues()

function schrijfTraag(blad, data) {
  for (let i = 0; i < data.length; i++) {
    blad.getRange(i + 1, 1).setValue(data[i].naam);
    blad.getRange(i + 1, 2).setValue(data[i].waarde);
  }
}

function schrijfSnel(blad, data) {
  const matrix = data.map(item => [item.naam, item.waarde]);
  blad.getRange(1, 1, matrix.length, matrix[0].length).setValues(matrix);
}
warning

Gebruik flush() spaarzaam

Roep SpreadsheetApp.flush() alleen aan het einde van een zware schrijfoperatie aan, of wanneer je de sheet tussentijds zichtbaar wilt bijwerken. Een flush() binnen een lus vertraagt je script drastisch, omdat het bij elke iteratie alle openstaande wijzigingen wegschrijft.

CacheService voor herhaalde berekeningen

Met CacheService voorkom je dat je dure berekeningen of externe aanroepen opnieuw uitvoert. De maximale bewaartijd is 6 uur (21600 seconden); zonder waarde gebruikt Apps Script standaard 600 seconden. Houd er rekening mee dat een bewaartijd slechts een suggestie is: bij veel data kan een item eerder worden verwijderd.

function berekenMetCache(invoer) {
  const cache = CacheService.getScriptCache();
  const cacheKey = `berekening_${invoer}`;

  const gecachede = cache.get(cacheKey);
  if (gecachede !== null) {
    return JSON.parse(gecachede);
  }

  const resultaat = zwareBerekening(invoer);

  cache.put(cacheKey, JSON.stringify(resultaat), 600);
  return resultaat;
}

function zwareBerekening(invoer) {
  Utilities.sleep(100);
  return invoer * 2;
}
lightbulb

Kies de juiste cache-scope

getScriptCache() deelt data tussen alle gebruikers van het script, getUserCache() is per gebruiker en getDocumentCache() is gekoppeld aan het actieve document. Elke waarde mag maximaal 100 KB groot zijn; gebruik voor grotere of langere opslag PropertiesService.

Service-aanroepen buiten lussen houden

function suboptimaal() {
  const blad = SpreadsheetApp.getActiveSheet();
  for (let i = 1; i <= 100; i++) {
    const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
    const naam = spreadsheet.getName();
    blad.getRange(i, 3).setValue(naam);
  }
}

function optimaal() {
  const blad = SpreadsheetApp.getActiveSheet();
  const naam = SpreadsheetApp.getActiveSpreadsheet().getName();
  const aantalRijen = blad.getLastRow();

  const data = Array(aantalRijen).fill([naam]);
  blad.getRange(1, 3, aantalRijen, 1).setValues(data);
}

Meerdere bladen tegelijk verwerken

function verwerkMeerdereBladen() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const bladen = ss.getSheets();

  const alleData = bladen.map(blad => ({
    naam: blad.getName(),
    data: blad.getDataRange().getValues(),
  }));

  const verwerkt = alleData.map(({naam, data}) => ({
    naam,
    totaal: data.slice(1).reduce((som, rij) => som + Number(rij[1] || 0), 0),
  }));

  const resultaatBlad = ss.getSheetByName('Samenvatting') || ss.insertSheet('Samenvatting');
  resultaatBlad.clearContents();
  resultaatBlad.getRange(1, 1, 1, 2).setValues([['Blad', 'Totaal']]);

  const outputData = verwerkt.map(r => [r.naam, r.totaal]);
  resultaatBlad.getRange(2, 1, outputData.length, 2).setValues(outputData);
}

Externe aanroepen parallel uitvoeren

Wanneer je meerdere HTTP-verzoeken moet doen, is UrlFetchApp.fetchAll() veel sneller dan een lus met losse fetch()-aanroepen, omdat de verzoeken parallel lopen.

function haalParallelOp(urls) {
  const requests = urls.map(url => ({url, muteHttpExceptions: true}));
  const responses = UrlFetchApp.fetchAll(requests);

  return responses.map(response => {
    if (response.getResponseCode() !== 200) {
      return null;
    }
    return JSON.parse(response.getContentText());
  });
}

Uitvoeringstijd meten

function metTijdsmeting(fn, naam) {
  const start = Date.now();
  const resultaat = fn();
  const duur = Date.now() - start;
  Logger.log(`${naam}: ${duur}ms`);
  return resultaat;
}

function benchmarkVergelijking() {
  const blad = SpreadsheetApp.getActiveSheet();
  const testData = Array.from({length: 500}, (_, i) => [i, i * 2, i * 3]);
  blad.getRange(1, 1, testData.length, 3).setValues(testData);

  metTijdsmeting(() => {
    let som = 0;
    for (let i = 1; i <= 500; i++) {
      som += blad.getRange(i, 2).getValue();
    }
    return som;
  }, 'Cel-voor-cel');

  metTijdsmeting(() => {
    const waarden = blad.getRange(1, 2, 500, 1).getValues();
    return waarden.reduce((s, r) => s + r[0], 0);
  }, 'Batch');
}
Hoeveel sneller is batch-lezen echt?

In tests is batch-lezen ongeveer 10 tot 100 keer sneller dan cel-voor-cel. Voor 500 rijen duurt cel-voor-cel typisch 8 tot 15 seconden, terwijl batch-lezen vaak onder de halve seconde blijft. De exacte cijfers hangen af van de belasting op de Google-servers, maar de orde van grootte is consistent.

Wanneer gebruik ik SpreadsheetApp.flush()?

Gebruik flush() als je tussentijds UI-feedback wilt tonen tijdens een lange operatie, of wanneer een vervolgaanroep afhankelijk is van wijzigingen die je net hebt weggeschreven. Roep het nooit binnen een lus aan.

Hoe optimaliseer ik externe API-aanroepen?

Gebruik UrlFetchApp.fetchAll() voor parallelle verzoeken, cache antwoorden met CacheService en batch je data zodat je minder verzoeken hoeft te doen.

Kan ik multi-threading gebruiken voor parallelle verwerking?

Nee, Apps Script is single-threaded. Je kunt wel fetchAll() gebruiken voor parallelle HTTP-aanroepen, maar je eigen JavaScript-code wordt altijd sequentieel uitgevoerd.

Hoe voorkom ik timeout-fouten bij grote datasets?

Een uitvoering mag standaard maximaal 6 minuten duren. Splits zware verwerking op in batches en gebruik een time-driven trigger of een doorlopende status in PropertiesService om het werk over meerdere runs te verdelen.

Bestaand script optimaliseren

  1. Meet de huidige uitvoertijd met tijdsmeting-wrappers.
  2. Identificeer de hotspot: zoek naar getValue() of setValue() in lussen.
  3. Vervang cel-voor-cel operaties door getValues() en setValues().
  4. Verplaats constante aanroepen zoals getActiveSpreadsheet() buiten lussen.
  5. Voeg CacheService toe voor herhaalde externe aanroepen.
  6. Meet opnieuw en vergelijk de resultaten.

Performance-optimalisatie in Apps Script komt vrijwel altijd op hetzelfde neer: verminder het aantal service-aanroepen door te batchen. Dit ene principe geeft de grootste verbetering en voorkomt timeout-fouten in productie.