引发自定义超时异常 [英] Throw custom timeout exception

查看:115
本文介绍了引发自定义超时异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个Google Apps Script网络应用程序("Web App"),该应用程序以用户身份执行,然后使用UrlFetchApp.fetch()通过Apps Script API调用另一个Apps Script项目("API Executable")中的各个功能,并执行它们以我的身份(请参阅获取用户有人像我一样运行Google Apps Script网络应用时提供的信息).

I have a Google Apps Script web app ("Web App") that executes as the user, then calls individual functions from another Apps Script project ("API Executable") via the Apps Script API using UrlFetchApp.fetch() and executes them as me (see Get user info when someone runs Google Apps Script web app as me).

此方法的局限性是UrlFetchApp.fetch()的超时时间为60s,而我的函数之一通常要花费更长的时间. API可执行函数成功完成运行,但是Web应用程序引发超时异常.我想通过运行第二个后续"函数来处理此异常,该函数查找并返回由原始函数成功创建的Google表格的URL.但是,我需要将传递给原始函数的参数之一传递给后续函数,而且看来我无法在标准try ... catch块中执行此操作.

A limitation of this method is that UrlFetchApp.fetch() has a 60s timeout, and one of my functions often takes longer than this. The API Executable function finishes running successfully, but the web app throws a timeout exception. I would like to handle this exception by running a second "follow-up" function that finds and returns the URL of the Google Sheet successfully created by the original function. However, I'll need to pass the follow-up function one of the parameters passed to the original function, and it appears I can't do this within a standard try...catch block.

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

My idea was to throw an exception that contains the needed parameter, but I can't figure out how to throw my own timeout exception; since Google Apps Script is synchronous, there's no way to track how long UrlFetchApp.fetch() has been running while it's running.

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

Is there a way to throw your own timeout exception? Or, is there another way I can pass the needed parameter to a function that executes if there's a timeout error?

我在这篇文章中也标记了Javascript,因为与Google Apps脚本有很多重叠之处,而且我认为这样做可以提高我与有答案的人联系的机会-希望可以.以下是我在网络应用中使用的调用API可执行函数的函数(如果有帮助的话).

I tagged Javascript in this post as well since there's a lot of overlap with Google Apps Script and I figured it would improve my chance of connecting with someone who has an answer--hope that's okay. Below is the function I'm using in my web app to call my API Executable functions, in case that's helpful.

基于@TheMaster的注释,我决定编写脚本,就像传递给executeAsMe()的参数被传递给catch()块一样,以查看发生了什么.我希望对opt_timeoutFunction未定义这一事实有一个例外,但是奇怪的是,它似乎只在catch()块的第一行正在运行,我不确定为什么.

Based on @TheMaster's comment, I decided to write the script as though parameters passed to executeAsMe() WERE being passed to the catch() block, to see what happened. I expected an exception regarding the fact the opt_timeoutFunction was undefined, but strangely it looks like only the first line of the catch() block is even running, and I'm not sure why.

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()'''块将异常抛出回客户端并在那里进行处理.

I ended up using the '''catch()''' block to throw an exception back to the client side and handle it there.

Google Apps脚本:

Google Apps Script:

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(简化版):

Client-side Javascript (a simplified version):

 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";
      }
    }

我确信必须有一种简洁/较简单的方法来做到这一点,但这是可行的!

I am sure there has to be a tidier/less complex way to do this, but this worked!

这篇关于引发自定义超时异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆