In my Google Sheets script, I am looking to introduce a 2-minute delay within the forEach loop for sending emails. Unfortunately, I have not had success

I am looking to introduce a 2-minute delay after each iteration of the loop. Specifically, I want to add a delay in the process of sending emails. You can find the complete code at this link.

   obj.forEach(function(row, rowIdx){
   sleep(1200000);
    // Emails will only be sent if the email_sent cell is empty and not hidden by a filter
    if (row[EMAIL_SENT_COL] == ''){
      try {
        const msgObj = fillInTemplateFromObject_(emailTemplate.message, row);

    // For more information on sending emails with unicode/emoji characters using GmailApp or MailApp, visit: https://developers.google.com/apps-script/reference/gmail/gmail-app#sendEmail(String,String,String,Object)
    GmailApp.sendEmail(row[RECIPIENT_COL], msgObj.subject, msgObj.text, {
      htmlBody: msgObj.html,
      attachments: emailTemplate.attachments,
      inlineImages: emailTemplate.inlineImages
    });
    
    out.push([new Date()]); // Record the date the email was sent
  } catch(e) {
    out.push([e.message]); // Record any errors encountered
  }
} else {
  out.push([row[EMAIL_SENT_COL]]); // Record that the email has already been sent
}
});

Answer №1

Solution:

To automate the task of sending emails periodically, you can create an Installable Trigger in Google Apps Script. This trigger will execute the function sendEmails() every minute once it is set up by following these steps:

NOTE: As a workaround for not being able to use everyMinutes(2), a method is suggested where the function runs every minute and checks a certain column to determine if an email needs to be sent.


function onOpen() {
  const ui = SpreadsheetApp.getUi();
  ui.createMenu('Mail Merge')
      .addItem('Send Emails', 'createTrigger')
      .addToUi();
}

function createTrigger() {
  ScriptApp.newTrigger("sendEmails")
    .timeBased()
    .everyMinutes(1)
    .create();
}

In order to optimize the process, replace the forEach() loop with a simple for loop that breaks out once the first email is marked for sending or has been sent successfully.


// Iterate through the rows of data and break when one email is sent
for (i = 0; i < obj.length; i++) {
    var row = obj[i];
    // Mark emails as "To Send" if email_sent cell is blank. Only send emails if email_sent cell is "To Send" and visible.
    if (row[EMAIL_SENT_COL] == ''){
        out.push(['To Send']);
        break;
    } else if (row[EMAIL_SENT_COL] == 'To Send'){
        try {
            const msgObj = fillInTemplateFromObject_(emailTemplate.message, row);

            GmailApp.sendEmail(row[RECIPIENT_COL], msgObj.subject, msgObj.text, {
                htmlBody: msgObj.html,
                attachments: emailTemplate.attachments,
                inlineImages: emailTemplate.inlineImages
            });

            out.push([new Date()]); // Record email sent date
        } catch(e) {
            out.push([e.message]); // Record error message
        }
        break;
    } else {
        out.push([row[EMAIL_SENT_COL]]);
    }
}

Once all rows are processed, delete the trigger to complete the task:


sheet.getRange(2, emailSentColIdx+1, out.length).setValues(out); // Update sheet with new data

if (out.length == obj.length) {
    var triggers = ScriptApp.getProjectTriggers();
    for (var j = 0; j < triggers.length; j++) {
        ScriptApp.deleteTrigger(triggers[j]);
    }
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

How can I dynamically display Material-UI's <MenuItem/> within a <DropDownMenu/> using ReactJS?

In my ReactJS + Material-UI project, I am working with an array named colors that contains different color strings such as "white", "blue", and "green. My goal is to render each color string as a <MenuItem/> within a <DropDownMenu/> component ( ...

Transmitting a Custom JS Object via Vue Router

Stuck! I’ve been hitting my head against the wall for hours trying to figure this out... Technologies Utilized: Vue (v3.0.0) Vue-Router (v4.0.0-0) Bootstrap (v5.1.1) The Objective: I have two pages (Home.vue, MetaUpdate.vue) and one component (Docum ...

Selenium throws an error message stating 'NoSuchAlertError: no alert is currently opened'

When using Selenium, I encounter an issue where clicking a button triggers an alert box, but accepting the alert box results in an error message. element.click(); driver.switchTo().alert().accept(); The problem is quite unpredictable. ...

`Creating the perfect bar``

Currently working on my resume using Angular and I am interested in incorporating a visual representation of my skill level in specific subjects. Came across something that caught my eye here: https://i.sstatic.net/84cir.png The challenge now is figuring ...

Bidirectional data connection in a Meteor application

I've developed a form-based application and I would like to allow users to fill out the form partially and return to it at a later time if needed. I've utilized iron router to create a unique URL for each instance of the form, enabling users to a ...

Is it possible to repeat this action by swiping to the left?

I'm currently developing an app in PhoneGap and retrieving information using JSON. My goal is to trigger this function again with Ajax when I slide left. This is the code I have so far. Thank you to everyone for your assistance. $(document).ready( ...

Using sinon.js version 1.10, jQuery version 2.1, and making synchronous requests

I have been attempting to simulate a server using sinon.js and calling it with jQuery.ajax, but I am facing issues getting it to work. Here is the code snippet: $(function() { var server = sinon.fakeServer.create(); server.respondWith('POST&apo ...

Selecting meteor.js user logout functionality

Is there a reliable method for detecting when a user logs out of the website? I have some maintenance tasks that need to be handled upon logout. The website is built using meteor.js user accounts. I will also be implementing validation features, so it&apo ...

Troubleshooting: How can I ensure my custom scrollbar updates when jQuery niceselect expands?

I am currently utilizing the jquery plugins mCustomScrollbar and niceselect. However, I have encountered an issue when expanding the niceselect dropdown by clicking on it - the mCustomScrollbar does not update accordingly. I suspect this is due to the abso ...

When the page is scrolled, trigger a click event on the next button in

Currently, I have a listing page that features pagination at the bottom. However, my goal is to transform this into an infinite loading page. The issue I am facing is that when the click event triggers after scrolling to the bottom, it makes calls for pag ...

InvalidSelectorError: The specified selector is not valid //button[contains(., 'buttonText')] while running JavaScript code

Here's an example of HTML code that I am working with: <button id="toolbar-item-17-update-id" type="submit" name="action" value="update" class="button-action-update btn btn-secondary"> Ed ...

fade in and out twice (for beginners)

Jquery <script> //Code snippet $(document).ready(function(){ $(".team").click(function(){ $(".panel").fadeToggle("3000"); }); $(".team").click(function(){ $(".teamh").fadeToggle("3000"); }); }); </script> HTM ...

Tips on displaying a selected choice | Utilizing Material UI Autocomplete

I have successfully fetched data from an API and used it as options in Material UI Autocomplete. However, when I select an option and send it back to the API using a POST request, the selected category is displayed as a number (0) instead of text, as shown ...

The Angular bootstrap popover vanishes as soon as the mouse hovers over it

Currently, I am facing an issue with an angular bootstrap popover on some text. The problem arises when the user tries to click on a link inside the popover as it disappears. Additionally, when changing from one popover to another, the previous one does no ...

Guide to incorporating a dynamic object within another object in JavaScript

I have fetched data from an API and organized it in a constant variable called res. Next, I am using a nested for loop to extract information about devices and countries. I create a variable that combines the names of these devices and countries, and then ...

Utilizing Props in Vue.js to Access Data for v-model

After browsing online, I attempted to pass props to data in the following manner: Child Component: props: { idInput: { type: String, required: false }, nameInput: { type: String, required: false }, }, data() { return { id: this.idInput, na ...

What is the best way to compare two strings without considering their capitalization?

Here is the issue I am facing: mytext = jQuery('#usp-title').val(); Next, I check if the text matches another element's content: if(jQuery("#datafetch h2 a").text() == mytext) { However, titles can vary in capitalization such as Space Mi ...

When trying to inject HTML into AngularJS elements, the data binding may not function

I attempted to reference the documentation but it appears that I may be overlooking something. My goal is to inject HTML that is connected to a JSON object. It functions correctly when the HTML is explicitly declared, however, upon injection and callin ...

Is there a way to retrieve the response body in Express framework?

In my NodeJS API using Express, I am attempting to save the response body of a request. To achieve this, I have created two middleware functions. app.use((req, res,next) => { res.status(404).json({ errors: [{ field: "url", ...

Improve performance by debouncing computed properties and getters in Vue

I'm having trouble getting debounce to work with computed properties and Vuex getters. The debounced functions are always returning undefined. Check out this JSFiddle for an example HTML: <div id="app"> <input v-model="text"> <di ...