如何返回对JSONP请求的即时响应并在

时间:2015-09-07 00:16:05

标签: javascript function google-apps-script jsonp google-spreadsheet-api

我正在使用JSONP从用户收集数据,但不要求用户得到回复。

因此,我希望向用户发送即时响应,以便他们可以继续而无需等待服务器处理。

如何向他们发送回复但继续处理?

我正在使用Google Script,但我想任何以javascript方式返回响应并继续处理也应该有效。

我有类似的东西:

function handleResponse(e) {
    //do something with e
    return ContentService
      .createTextOutput('console.log("updated")')
      .setMimeType(ContentService.MimeType.JAVASCRIPT);
}

我想返回回复,然后“用e做点什么”。

编辑:好的经过多次捣乱我有一个半工作的解决方案(总是有路障!)

目前我有:

var SCRIPT_PROP = PropertiesService.getScriptProperties();
function doGet(e){
    SCRIPT_PROP.setProperty("myParameters", e.parameters);
    ScriptApp.newTrigger("handleResponse")
      .timeBased()
      .after(20 * 1000)
      .create();
    return ContentService
      .createTextOutput('console.log("processing")')
      .setMimeType(ContentService.MimeType.JAVASCRIPT);
}
function handleResponse() {
    Logger.log(SCRIPT_PROP.getProperty("myParameters"));
}

它正在做的是将数据从用户保存到类似全局的变量。 然后它设置一个触发器,在20秒后执行handleResponse()函数。 最后它向用户返回一些内容,因此他们不必等待handleResponse()函数完成。

现在我遇到了这个解决方案遇到的问题,它似乎是命中和错过,它有时会激活handleResponse()函数,有时候也不会这样做。

从文档中可以看出触发器将在您指定的时间执行+/- 15分钟!现在,在它运作的时代,我已经看到它需要10秒到45秒。在没有工作的时候,我等了20分钟,但仍然没有。似乎我设置触发器的时间越短,它从未执行的次数就越多。

我遇到的另一个问题是我一次只能有14个触发器,所以如果他们决定花15分钟执行,我可以轻松达到这个限制。

还有其他办法让这样的解决方案起作用吗?

5 个答案:

答案 0 :(得分:2)

如果唯一的要求是停止前端的窗口加载操作,如评论中所阐明的那样,那么考虑将脚本标记插入包装在setTimeout() ...

function createScriptTag() {

  setTimeout( function() {  // throw task in JS queue to prevent window load
    var scriptTag = document.createElement('script');
    scriptTag.src = "SCRIPT_URL?prefix=myCallback"; // include parameters if needed
    document.body.appendChild(scriptTag);
  }, 0);

}

createScriptTag();

答案 1 :(得分:2)

如果用户正在查看的页面是通过Google Apps脚本提供的,则可以使用google.script.run对服务器端应用程序进行异步调用。

请参阅:https://developers.google.com/apps-script/guides/html/reference/run

答案 2 :(得分:1)

我会使用'tasks'创建属性,并且每分钟运行一个触发器,只检查需要完成的操作。

触发器:

var tasks = SCRIPT_PROP.getProperties() 

for (var key in tasks) {
handleTasks(tasks[key]);
SCRIPT_PROP.deleteProperty(key);
}

答案 3 :(得分:1)

您可以做什么(如果您不介意批量处理数据)只有一个timeBased everyMinutes触发器,它会调用函数来处理您的所有数据。当然,如果您需要快速处理数据(例如提交后5-10秒),那么它可能不适合您。

您也不必使用PropertiesService,我想只使用全局数组会更简单。将新数据推入其中,当触发函数运行时,从阵列中删除所有已处理的条目。

当然,这不是一个完美的解决方案,但遗憾的是,如果不使用Google的sandboxed HTML preprocessed outputs,它就会得到尽可能好的效果。这样的页面可以访问google.script.run功能,这将解决您的问题。但正如我所看到的,您正在运行独立的类似API的脚本,因此这对您不起作用。

答案 4 :(得分:1)

您需要的是消息排队系统。 Google云平台内置了一个名为Pub / Sub的内置功能。这种方法不是一个简单的设置,也不是太难。事实上,90%的设置可以在API控制台界面中完成。可以使用我的github上找到的pub / sub库来处理最后一位 https://github.com/Spencer-Easton/Apps-Script-PubSubApp-Library

基本概要如下:

1) User submission->Form Collection adds response to pub/sub responseQueue->Form Collection responds to form submit  

2) Pub/Sub responseQueue->Form Processor preforms business logic->Form Processor adds response to processedQueue or Audit Log.

注意:步骤1和2彼此异步运行。

表单集合脚本
1)添加pubsub库:Mk1rOXBN8cJD6nl0qc9x5ukMLm9v2IJHf
2)添加GSApp库:MJ5317VIFJyKpi9HCkXOfS0MLm9v2IJHf
3)打开脚本Developers Console Project   --a)添加pub / sub api。
  --b)在凭据下添加服务帐户。下载密钥为json。
  --c)打开大数据 - >导航中的发布/订阅。菜单
  --d)创建创建主题:responseQueue
  --e)保持打开状态,因为我们将在稍后的表单处理脚本中为此主题设置权限 4)将jsonKey的内容复制到脚本属性,将其保存为jsonKey
5)添加代码片段:

function getTokenService(){
  var jsonKey = JSON.parse(PropertiesService.getScriptProperties().getProperty("jsonKey"));  
  var privateKey = jsonKey.private_key;
  var serviceAccountEmail = jsonKey.client_email; 
  var sa = GSApp.init(privateKey, ['https://www.googleapis.com/auth/pubsub'], serviceAccountEmail);
  sa.addUser(serviceAccountEmail)
  .requestToken();
  return sa.tokenService(serviceAccountEmail);
}

6)下面是一个示例doGet()函数:

function doGet(e){
  try{
    PubSubApp.setTokenService(getTokenService());
    //Don't forget to set this to your scripts projectID      
    var pub = PubSubApp.PublishingApp('api-project-YourAPIProjectID');                     
    var message = pub.newMessage();
    message.data = Utilities.base64Encode(e.parameter.response);
    pub.getTopic('responseQueue').publish(message);
    return ContentService
  .createTextOutput(e.parameter.callback+'(console.log("processing"))')
  .setMimeType(ContentService.MimeType.JAVASCRIPT);
  }catch(e){throw new Error(e)}
}

表单处理器脚本
1)添加此示例代码片段:

function doPost(e) {
  var postBody = JSON.parse(e.postData.getDataAsString());
  var messageData = Utilities.newBlob(Utilities.base64Decode(postBody.message.data)).getDataAsString();  
  //Spreadsheet is used for an audit log. Add you own spreadshhetid here.     
  var ss = SpreadsheetApp.openById(SpreadSheetId).getSheetByName("Log");
  ss.appendRow([new Date(), messageData, JSON.stringify(postBody,undefined,2)])
  return 200;
}

2)将脚本发布为Web应用程序
3)将脚本部署到Chrome Store。 (你可以把它留在草稿模式)
4)获取脚本部署的URL并保存以供日后使用。它看起来像:

https://script.google.com/a/macros/{DOMAIN}/s/AKfycbyEeHW32Pa...5gLHa/exec

5)打开脚本Developers Console Project   --a)添加pub / sub api。
  --b)在凭据下添加服务帐户。下载密钥为json。
6)返回Form Collection Dev Console
--a)将步骤5中的URL添加到API&验证 - >推 - >添加域
--b)在From Collection Dev Console Pub / Sub设置中,将步骤5中的服务帐户电子邮件作为订户添加到responseQueue权限。
--c)单击responseQueue主题上的Add Subscriber。为订阅提供一个令人难忘的名称,如formProcessorOne。选择推送,然后输入您在步骤4中获得的相同URL --d)点击More Options在确认截止日期前输入超时限制。这是您希望表单处理脚本完成的时间量。如果截止日期没有确认,则消息将返回队列。

<强>最后
当消息发布到您的表单收集器时,响应参数将添加到responseQueue。 responseQueue将消息推送到From Processor脚本。   注意:根据您在Form Processor上的流量和业务逻辑的长度,您可能会收到太多的并发脚本错误。永远不要害怕,因为您设置了超时消息返回队列并将再次尝试。

因此,根据您的缩放需求,这可能对您的项目而言可能过度。这个帖子中的其他答案也是合理的。如果您期望使用属性服务的低音量,因为工作队列可以正常锁定。

同样使用HtmlService来托管你的html,你可以访问类似于jquerys $ .get()的google.script.run,但是可以透明地保持对脚本的访问和身份验证。