title: Reduce script run time in Google Apps Script?
body: <p>I created a script that tracks attendance for distance learning. After a while it times out so I think I am having issues with too many calls to the Google Classroom API, however I don't see a way that I can change it to take those calls out of a loop.</p>
  <p>The script takes all the Google Classroom classes that my apps script account is a co-teacher on and using timed triggers creates a daily attendance assignment with one question that says 'here'. Students are then supposed to answer the question and then another trigger at night runs the function to 'grade' each assignment and populate my spreadsheet so school secretaries can view it in the morning and record the previous days attendance.</p>
  <p>The part that seems to have the bottleneck is my getStudentResponses() function. I tried to reduce time by filtering out students that didn't submit the assignment, but it wasn't enough. Does anyone see any way that I can make this faster? I was reading up on using the Cache Service, but I couldn't figure out how to get that to work. Any help would be appreciated.</p>
  <pre><code>var ss = SpreadsheetApp.getActive();
  var date = new Date();
  
  /*
  creates a button to programmatically create all necessary timed triggers for easy deployment
  */
  function onOpen() {
    var ui = SpreadsheetApp.getUi();
    ui.createMenu('Attendance')
        .addItem('Create Triggers', 'createTriggers')
        .addToUi();
  }
  
  /*
  auto accepts any co-teacher invites
  */
  function acceptInvite() {
    try{
      var optionalArgs = {
        userId: &quot;me&quot;
      };
      var invites = Classroom.Invitations.list(optionalArgs);
      for(var i = 0; i &lt; invites.invitations.length; i++) {
        Classroom.Invitations.accept(invites.invitations[i].id);
      }
    }
    catch(e){}
  }
  
  /*
  populates a spreadsheet with all the classes that the script Google account is a co-teacher of
  the sheet has two columns one with the course name and two with the course id
  */
  function listCourses() {
    var optionalArgs = {courseStates: &quot;ACTIVE&quot;};
    var response = Classroom.Courses.list(optionalArgs);
    var courses = response.courses;
    var classSheet;
    
    try{
      classSheet = ss.insertSheet(&quot;Classes&quot;, 0);
      ss.insertSheet(&quot;Assignments&quot;, 1);
    }
    catch(e) {
      classSheet = ss.getSheetByName(&quot;Classes&quot;);
    }
    classSheet.clear();
    if (courses &amp;&amp; courses.length &gt; 0) {
      for (i = 0; i &lt; courses.length; i++) {
        var course = courses[i];
        classSheet.appendRow([course.name, course.id]);
      }
    }
  }
  
  /*
  reads the sheet to get all the classes and creates a new array with all the class IDs
  */
  function getCourses() {
    var classSheet = ss.getSheetByName(&quot;Classes&quot;);
    var classList = new Array();
    var range = classSheet.getDataRange();
    var values = range.getValues();
    
    for(var i in values) {
      var row = values[i];
      var courseId = row[1]+&quot;&quot;;
      classList.push(Classroom.Courses.get(courseId));
    }
    createTopics(classList);
  }
  
  /*
  called immediatly after getCourses, creates topics in each class that will contain the daily attendance assignment
  */
  function createTopics(classList) {
    for(i = 0; i &lt; classList.length; i++) {
      var topic;
      var resource = {name: &quot;Daily Online Attendance&quot;};
      
      try {
        topic = Classroom.Courses.Topics.create(resource, classList[i].id);
        createAssignment(topic,classList[i]);
      }
      catch(e) {
        if(e == &quot;GoogleJsonResponseException: API call to classroom.courses.topics.create failed with error: Requested entity already exists&quot;) {
          var topics = Classroom.Courses.Topics.list(classList[i].id);
  
          for(j = 0; j &lt; topics.topic.length; j++) {
            if(topics.topic[j].name == &quot;Daily Online Attendance&quot;) {
              createAssignment(topics.topic[j], classList[i]);
            }
          }
        }
      }
    }
  }
  
  /*
  creates an assignment in each class, under each topic
  each assignment only has one choice that says &quot;here&quot; and is going to be 'graded' each night to track attendance
  */
  function createAssignment(topic,course) {
    var resource = {
      title: &quot;Attendance for &quot;+(date.getMonth()+1)+&quot;/&quot;+date.getDate()+&quot;/2020&quot;,
      description: &quot;Please fill this assignment out each day for attendance&quot;,
      topicId: topic.topicId,
      state: &quot;PUBLISHED&quot;,
      workType: &quot;MULTIPLE_CHOICE_QUESTION&quot;,
      multipleChoiceQuestion: {
        &quot;choices&quot;: [
          &quot;Here&quot;
        ] 
      }
    };
    try {
      var assignment = Classroom.Courses.CourseWork.create(resource, course.id);
      var sheet = ss.getSheetByName(&quot;Assignments&quot;);
      sheet.appendRow([course.id,assignment.id]);
    }
    catch(e){}
  }
  
  /*
  creates a new sheet for each day and logs each assignement
  */
  function getStudentResponses() {
    var assignmentSheet = ss.getSheetByName(&quot;Assignments&quot;);
    var sheet2;
    var response;
  
    assignmentSheet.sort(1, true);
    
    try{
      sheet2 = ss.insertSheet(&quot;Attendance for &quot;+(date.getMonth()+1)+&quot;/&quot;+date.getDate()+&quot;/2020&quot;,(ss.getSheets().length-(ss.getSheets().length-2)));
      sheet2.appendRow([&quot;Student Last Name&quot;,&quot;Student First Name&quot;,&quot;Grade&quot;,&quot;Class Name&quot;,&quot;Assignment Answer&quot;]);
    }
    catch(e) {
      sheet2 = ss.getSheetByName(&quot;Attendance for &quot;+(date.getMonth()+1)+&quot;/&quot;+date.getDate()+&quot;/2020&quot;);
    }
    
    sheet2.setFrozenRows(1);
    
    var range = assignmentSheet.getDataRange();
    var values = range.getValues();
    
    for(var i in values) {
      var row = values[i];
      var courseId = row[0]+&quot;&quot;;
      var courseWorkId = row[1]+&quot;&quot;;
      
      try {
        response = Classroom.Courses.CourseWork.StudentSubmissions.list(courseId, courseWorkId);
          
        for(var j in response.studentSubmissions) {
          if(response.studentSubmissions[j].state == &quot;TURNED_IN&quot;) {
            try {
              var grade;
              var email = Classroom.UserProfiles.get(response.studentSubmissions[j].userId).emailAddress;
              sheet2.appendRow([Classroom.UserProfiles.get(response.studentSubmissions[j].userId).name.familyName,Classroom.UserProfiles.get(response.studentSubmissions[j].userId).name.givenName,grade,Classroom.Courses.get(courseId).name,response.studentSubmissions[j].multipleChoiceSubmission.answer]);
            }
            catch (e) {}
          }
        }
      }
      catch(e) {}
    }
  }
  
  /*
  deletes all assignemnts that were created
  */
  function deleteAssignments() {
    var assignmentSheet = ss.getSheetByName(&quot;Assignments&quot;);
    assignmentSheet.sort(1, true);
    var range = assignmentSheet.getDataRange();
    var values = range.getValues();
    
    for(var i in values) {
      var row = values[i];
      var courseId = row[0]+&quot;&quot;;
      var courseWorkId = row[1]+&quot;&quot;;
      
      try {
        Classroom.Courses.CourseWork.remove(courseId, courseWorkId);
      }
      catch(e) {}
    assignmentSheet.clear();
    }
  }
  
  function createTriggers() {
    ScriptApp.newTrigger('getCourses')
        .timeBased()
        .everyDays(1)
        .atHour(6)
        .create();
    
    ScriptApp.newTrigger('getStudentResponses')
        .timeBased()
        .everyDays(1)
        .atHour(22)
        .create();
    
    ScriptApp.newTrigger('deleteAssignments')
        .timeBased()
        .everyDays(1)
        .atHour(23)
        .create();
    
    ScriptApp.newTrigger('listCourses')
        .timeBased()
        .everyDays(1)
        .atHour(21)
        .create();
    
    ScriptApp.newTrigger('acceptInvite')
        .timeBased()
        .everyDays(1)
        .atHour(20)
        .create();
  }
  </code></pre>