在 Google Apps 脚本中从服务器端与客户端通信

Communicate from server to client side in Google Apps script

我正在尝试编写一个 Google 具有客户端和服务器端组件的 Apps 脚本。客户端组件显示进度条。客户端调用服务器端函数(异步调用),其进度必须显示在客户端进度条中。现在,我想要的是能够根据服务器端功能的反馈更新客户端进度条。这可能吗?

由于 JS 异步进行服务器端调用,因此我无法真正在客户端循环调用函数和更新进度条。

我当然可以将服务器端函数的执行分成多个步骤,从客户端一个一个地调用,每次更新状态栏。但我想知道是否有更好的解决方案。有没有办法从服务器端调用客户端函数,并根据传递的参数更新进度条?或者有没有办法从服务器端访问客户端进度条对象并修改它?

与其将工作请求和进度更新捆绑在一起,我建议您将这两个问题分开。

在服务器端,应客户端请求执行工作的函数应该更新状态存储;例如,这可能是一个 ScriptProperty。工作功能在完成工作之前不需要响应客户端。服务端也应该有一个函数,可以被客户端调用,简单的报告当前的进度。

当客户端第一次调用服务器请求工作时,它也应该调用进度报告器。 (大概,第一次调用将得到 0% 的结果。)状态调用的 onSuccess 处理程序可以更新您用来表达进度的任何视觉效果,然后再次调用服务器的进度报告器,使用本身作为成功处理程序。当然,这应该延迟完成。

当进度达到100%或工作完成时,客户端的进度检查器可以关闭。

我处理这个问题的方法是让中间人(现在向 Romain Vialard 大声疾呼这个想法)处理进度:Firebase

HTML/client 端可以连接到您的 Firebase 帐户(它们是免费的!)并 "watch" 进行更改。

客户端代码可以在代码执行过程中更新数据库——这些更改会立即通过 Firebase 反馈到 HTML 页面。这样,您就可以更新进度条。

罗曼有一个小example/descriptionhere

我使用的代码:

//Connect to firebase
var fb = new Firebase("https://YOUR_DATABASE.firebaseio.com/");
//Grab the 'child' holding the progress info
var ref = fb.child('Progress');
//When the value changes
ref.on("value", function(data) {
  if (data.val()) {
    var perc = data.val() * 100;
    document.getElementById("load").innerHTML = "<div class='determinate' style='width:" + perc + "%\'></div>";
  }
});

在客户端,我使用Firebase library来更新进度:

var fb = FirebaseApp.getDatabaseByUrl("https://YOUR_DATABASE..firebaseio.com/");
var data = { "Progress": .25};
fb.updateData("/",data);

基于 Jens 的方法,您可以使用 CacheService 作为数据代理,而不是外部服务。我采用的方法是让我的 "server" 应用程序生成一个临时缓存键,它 returns 到 "client" 应用程序的成功回调。然后,客户端应用程序每隔一段时间轮询此缓存键,以查看服务器应用程序是否已将结果返回到缓存中。

服务器应用程序 returns 一个临时缓存键并包含一些辅助函数以简化在客户端的检查:

function someAsynchronousOperation() {
  var interimCacheKey = createInterimCacheKey();

  doSomethingComplicated(function(result) {
      setCacheKey(interimCacheKey, result);
  });

  return interimCacheKey;
}

function createInterimCacheKey() {
  return Utilities.getUuid();
}

function getCacheKey(cacheKey, returnEmpty) {
  var cache = CacheService.getUserCache();
  var result = cache.get(cacheKey);

  if(result !== null || returnEmpty) {
    return result;
  }
}

function setCacheKey(cacheKey, value) {
  var cache = CacheService.getUserCache();

  return cache.put(cacheKey, value);
}

Note that by default getCacheKey doesn't return. This is so that google.script.run's successHandler doesn't get invoked until the cache entry returns non-null.

在客户端应用程序中(我在其中使用 Angular),您在服务器中调用异步操作,并等待其结果:

google.script.run.withSuccessHandler(function(interimCacheKey) {
  var interimCacheCheck = $interval(function() {
    google.script.run.withSuccessHandler(function(result) {
      $interval.cancel(interimCacheCheck);
      handleSomeAsynchronousOperation(result);
    }).getCacheKey(interimCacheKey, false);
  }, 1000, 600); // Check result once per second for 10 minutes
}).someAsynchronousOperation();

使用这种方式你也可以报告进度,只有在进度达到100%后才取消你的检查。在这种情况下,您需要消除间隔到期时间。