javascript - 抛出自定义超时异常
问题描述
我有一个以用户身份执行的 Google Apps Script 网络应用程序(“Web App”),然后通过 Apps Script API 使用UrlFetchApp.fetch()
并以我的身份执行它们从另一个 Apps Script 项目(“API Executable”)调用各个函数(请参阅获取用户当有人以我的身份运行 Google Apps Script Web 应用程序时的信息)。
这种方法的一个限制是UrlFetchApp.fetch()
有 60 秒的超时,而我的一个函数通常需要比这更长的时间。API Executable 函数成功运行完成,但 Web 应用程序抛出超时异常。我想通过运行第二个“后续”函数来处理此异常,该函数查找并返回由原始函数成功创建的 Google 表格的 URL。但是,我需要将后续函数作为传递给原始函数的参数之一传递,看来我无法在标准的 try...catch 块中执行此操作。
我的想法是抛出一个包含所需参数的异常,但我不知道如何抛出我自己的超时异常;由于 Google Apps 脚本是同步的,因此无法跟踪它UrlFetchApp.fetch()
在运行时运行了多长时间。
有没有办法抛出自己的超时异常?或者,是否有另一种方法可以将所需的参数传递给在出现超时错误时执行的函数?
我也在这篇文章中标记了 Javascript,因为与 Google Apps Script 有很多重叠,我认为它会增加我与有答案的人联系的机会——希望没关系。下面是我在我的网络应用程序中使用的函数来调用我的 API 可执行函数,以防万一。
编辑:根据@TheMaster 的评论,我决定编写脚本,就好像传递给executeAsMe()
WERE 的参数被传递给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()''' 块将异常抛出回客户端并在那里处理它。
谷歌应用脚本:
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";
}
}
我确信必须有一种更整洁/不太复杂的方法来做到这一点,但这有效!
推荐阅读
- sql - 我也不明白 ORA-01779 指的是什么列
- python - Python Plotnine - 创建堆积条形图
- sql-server - 优化单个表上具有多个连接的查询
- html - 选择 html 标记中任何位置具有文本徽标的 img 标记
- c++ - 使用 GStreamer 和 NVENC 编码 OpenGL 纹理?
- c++ - 使用 GCC 编译但与 LLVM LLD 链接时,LTO 是否有效?
- asp.net-ajax - 如何以表格格式获取所选数据?
- mips - 尝试在 0x00400024 处执行非指令。Qtspim 中的错误代码
- mysql - 在mysql中创建动态过程调用语句
- python - 如何打印 YAML 字符串的特定部分