抛出自定义超时异常

Throw custom timeout exception

我有一个以用户身份执行的 Google Apps 脚本网络应用程序 ("Web App"),然后通过 Apps 脚本从另一个 Apps 脚本项目 ("API Executable") 调用各个函数API 使用 UrlFetchApp.fetch() 并像我一样执行它们(参见 )。

此方法的一个限制是 UrlFetchApp.fetch() 有 60 秒的超时,而我的一个函数通常需要比这更长的时间。 API 可执行函数成功完成 运行,但 Web 应用程序抛出超时异常。我想通过 运行 第二个 "follow-up" 函数来处理这个异常,该函数找到并 returns Google Sheet 成功创建的 URL原来的功能。但是,我需要将传递给原始函数的参数之一传递给后续函数,看来我无法在标准 try...catch 块中执行此操作。

我的想法是抛出一个包含所需参数的异常,但我不知道如何抛出我自己的超时异常;由于 Google Apps 脚本是同步的,因此无法跟踪 UrlFetchApp.fetch() 已经 运行 运行.

有没有办法抛出你自己的超时异常?或者,是否有另一种方法可以将所需的参数传递给在出现超时错误时执行的函数?

我也在这个 post 中标记了 Javascript,因为与 Google Apps 脚本有很多重叠,我认为这会增加我与拥有回答--希望没关系。下面是我在我的网络应用程序中用来调用我的 API 可执行函数的函数,以防有帮助。

编辑:根据@TheMaster 的评论,我决定编写脚本,就像传递给 executeAsMe() 的参数被传递给 catch() 块一样,看看发生了什么。关于 opt_timeoutFunction 未定义这一事实,我预计会出现异常,但奇怪的是,看起来只有 catch() 块的第一行甚至是 运行,我不确定为什么。

function executeAsMe(functionName, paramsArray, opt_timeoutFunction, opt_timeoutParams) {
  try {
    console.log('Using Apps Script API to call function ' + functionName.toString() + ' with parameter(s) ' + paramsArray.toString());

    var url = 'https://script.googleapis.com/v1/scripts/Mq71nLXJPX95eVDFPW2DJzcB61X_XfA8E:run';

    var payload = JSON.stringify({"function": functionName, "parameters": paramsArray, "devMode": true})

    var params = {method:"POST",
                  headers: {Authorization: 'Bearer ' + getAppsScriptService().getAccessToken()},
                  payload:payload,
                  contentType:"application/json",
                  muteHttpExceptions:true};

    var results = UrlFetchApp.fetch(url, params);
    var jsonResponse = JSON.parse(results).response;
    if (jsonResponse == undefined) {
      var jsonResults = undefined;
    } else {
      var jsonResults = jsonResponse.result;
    }
  } catch(error) {
    console.log('error = ' + error); // I'm seeing this in the logs...
    console.log('error.indexOf("Timeout") = ' + error.indexOf("Timeout").toString); // ...but not this. It skips straight to the finally block
    if (error.indexOf('Timeout') > 0) { // If error is a timeout error, call follow-up function
      console.log('Using Apps Script API to call follow-up function ' + opt_timeoutFunction.toString() + ' with parameter(s) ' + paramsArray.toString());

      var url = 'https://script.googleapis.com/v1/scripts/Mq71nLXJPX95eVDFPW2DJzcB61X_XfA8E:run';

      var payload = JSON.stringify({"function": opt_timeoutFunction, "parameters": opt_timeoutParams, "devMode": true})

      var params = {method:"POST",
                    headers: {Authorization: 'Bearer ' + getAppsScriptService().getAccessToken()},
                    payload:payload,
                    contentType:"application/json",
                    muteHttpExceptions:true};

      var results = UrlFetchApp.fetch(url, params);
      var jsonResponse = JSON.parse(results).response;
      if (jsonResponse == undefined) {
        var jsonResults = undefined;
      } else {
        var jsonResults = jsonResponse.result;
      }
    }
  } finally {
    console.log('jsonResults = ' + jsonResults);
    return jsonResults;
  }

}

我最终使用“'catch()'”块将异常抛回客户端并在那里进行处理。

Google 应用程序脚本:

function executeAsMe(functionName, paramsArray) {
  try {
    console.log('Using Apps Script API to call function ' + functionName.toString() + ' with parameter(s) ' + paramsArray.toString());

    var url = 'https://script.googleapis.com/v1/scripts/Mq71nLXJPX95eVDFPW2DJzcB61X_XfA8E:run';

    var payload = JSON.stringify({"function": functionName, "parameters": paramsArray, "devMode": true})

    var params = {method:"POST",
                  headers: {Authorization: 'Bearer ' + getAppsScriptService().getAccessToken()},
                  payload:payload,
                  contentType:"application/json",
                  muteHttpExceptions:true};

    var results = UrlFetchApp.fetch(url, params);
    var jsonResponse = JSON.parse(results).response;
    if (jsonResponse == undefined) {
      var jsonResults = undefined;
    } else {
      var jsonResults = jsonResponse.result;
    }
    return jsonResults;
  } catch(error) {
    console.log('error = ' + error);
    if (error.toString().indexOf('Timeout') > 0) {
      console.log('Throwing new error');
      throw new Error('timeout');
    } else {
      throw new Error('unknown');
    }
  } finally {
  }
}

客户端Javascript(简化版):

 function createMcs() {
      var userFolder = getDataFromHtml().userFolder;    

      google.script.run
        .withSuccessHandler(createMcsSuccess)
        .withFailureHandler(createMcsFailure)
        .withUserObject(userFolder)
        .executeAsMe('createMasterCombinedSchedule', [userFolder]);
    }

    function createMcsSuccess(mcsParameter) {
      if (mcsParameter == undefined) {
        simpleErrorModal.style.display = "block"; // A generic error message
      } else {
        document.getElementById("simpleAlertHeaderDiv").innerHTML = 'Master Combined Schedule Created Successfully';
        document.getElementById("simpleAlertBodyDiv").innerHTML = 'Your Master Combined Schedule was created successfully. Click <a href="' + mcsParameter + '" target="_blank">here</a> to view.';
        simpleAlertModal.style.display = "block";
      }
    }

    function createMcsFailure(mcsError, userFolder, counter) { // The exception I threw will activate this function
      if (!counter) { // Added a counter to increment every time checkForCreatedMcs() runs so it doesn't run indefinitely
        var counter = 0;
      }
      if (mcsError.message == 'Error: timeout' && counter < 5) { // If timeout error, wait 10s and look for MCS URL
        window.setTimeout(checkForCreatedMcs(mcsError, userFolder, counter), 10000);
      } else if (mcsError.message == 'Error: timeout' && counter == 5) { // If it hasn't worked after 5 tries, show generic error message
        simpleErrorModal.style.display = "block";
      } else { // For any error that's not a timeout exception, show generic error message
        simpleErrorModal.style.display = "block"; 
      }
    }

    function checkForCreatedMcs(mcsError, userFolder, counter) {
      counter++;
      google.script.run
        .withSuccessHandler(checkForCreatedMcsSuccess)
        .withUserObject([mcsError, userFolder, counter])
        .executeAsMe('checkIfMcsExists', [userFolder]); // checkIfMcsExists() is a pre-existing function in my API Executable project I had already used elsewhere      
    }

    function checkForCreatedMcsSuccess(mcsExistsParameter, params) {
      var mcsError = params[0];
      var userFolder = params[1];
      var counter = params[2];
      if (mcsExistsParameter == undefined) { // If function returns undefined, show generic error message
        simpleErrorModal.style.display = "block";
      } else if (mcsExistsParameter == false) { // If function returns false, wait 10s and try again
        createMcsFailure(mcsError, userFolder, counter);
      } else { // If function returns URL, show success modal with link
        document.getElementById("simpleAlertHeaderDiv").innerHTML = 'Master Combined Schedule Created Successfully';
        document.getElementById("simpleAlertBodyDiv").innerHTML = 'Your Master Combined Schedule was created successfully. Click <a href="' + mcsExistsParameter + '" target="_blank">here</a> to view.';
        simpleAlertModal.style.display = "block";
      }
    }

我确信必须有一个 tidier/less 复杂的方法来做到这一点,但这个方法奏效了!