异步执行功能App脚本

时间:2018-12-18 00:22:29

标签: asynchronous google-apps-script callback promise appscript

我一直在研究,找不到关于如何在Google App脚本中使用异步函数的参考或文档,我发现人们提到这是可能的,但没有提到如何...

有人能指出我正确的方向还是给我一个例子? 承诺,回调或其他可以帮助我解决此问题的方法。

我有此函数,可以将其称为foo,这需要花费一些时间才能执行(时间长到可能使http调用超时)。

我想做的就是重构,就像这样

function doPost(e) {
    // parsing and getting values from e
    var returnValue = foo(par1, par2, par3);
      return ContentService
             .createTextOutput(JSON.stringify(returnValue))
             .setMimeType(ContentService.MimeType.JSON);
}

function foo(par1, par2, par3) {
    var returnValue = something(par1, par2, par3); // get the value I need to return;

    // continue in an Async way, or schedule execution for something else
    // and allow the function to continue It's flow

    return returnValue;
}

现在,我想意识到foo中的那个位,因为它花了很长时间,而且我不想冒险超时,而且在那里发生的逻辑完全是与客户无关的,所以它不会事情,我只需要返回值就可以了。

另外,我认为值得一提的是,它已作为网络应用程序部署在Google Drive中。

4 个答案:

答案 0 :(得分:4)

还有另一种方法可以完成此任务。

您可以使用time-based one-off triggers异步运行函数,它们花一些时间排队(30-60秒),但是对于要从主执行中删除的慢速运行任务是理想的选择您的脚本。

// Creates a trigger that will run a second later
ScriptApp.newTrigger("myFunction")
  .timeBased()
  .after(1)
  .create();

我整理了一个方便的脚本,称为Async.gs,可帮助您删除该技术的样板。您甚至可以使用它通过CacheService传递参数。

这里是链接:

https://gist.github.com/sdesalas/2972f8647897d5481fd8e01f03122805

// Define async function
function runSlowTask(user_id, is_active) {
  console.log('runSlowTask()', { user_id: user_id, is_active: is_active });
  Utilities.sleep(5000);
  console.log('runSlowTask() - FINISHED!')
}

// Run function asynchronously
Async.call('runSlowTask');

// Run function asynchronously with one argument
Async.call('runSlowTask', 51291);

// Run function asynchronously with multiple argument
Async.call('runSlowTask', 51291, true);

// Run function asynchronously with an array of arguments
Async.apply('runSlowTask', [51291, true]);

// Run function in library asynchronously with one argument
Async.call('MyLibrary.runSlowTask', 51291);

// Run function in library asynchronously with an array of arguments
Async.apply('MyLibrary.runSlowTask', [51291, true]);

答案 1 :(得分:2)

使用新的 V8 runtime,现在可以编写 async functions 并在您的应用脚本中使用 promise。

甚至 triggers 也可以声明为异步!例如(打字稿):

async function onOpen(e: GoogleAppsScript.Events.SheetsOnOpen) {
    console.log("I am inside a promise");
    // do your await stuff here or make more async calls
}

要开始使用新的运行时,只需遵循此 guide。简而言之,这一切都归结为将以下行添加到您的 appsscript.json 文件中:

{
  ...
  "runtimeVersion": "V8"
}

答案 2 :(得分:1)

  • 您要通过使用Google Apps脚本的异步处理来执行功能。
  • 您要使用时间触发器以异步处理方式运行这些函数。

不幸的是,如果我的理解是正确的,则没有直接实现它的方法和官方文档。但是,作为一种解决方法,可以同时使用Google Apps Script API和可以通过异步处理工作的fetchAll方法来实现。

此替代方法的流程如下。

  1. 部署API可执行文件,启用Google Apps Script API。
  2. 使用fetchAll,请求Google Apps Script API的端点运行功能。
    • 一次请求多个函数时,这些函数将通过fetchAll进行异步处理。

注意:

  • 我认为也可以使用Web Apps代替Google Apps Script API。
  • 为了简单地使用此解决方法,我创建了一个GAS library。我认为您也可以使用它。
  • 在这种解决方法中,您还可以使用时间触发器通过异步处理运行函数。

参考文献:

如果我误解了你的问题,对不起。

答案 3 :(得分:0)

我基于Tanaike's answer创建了它的另一个版本。我的目标是:

  • 易于维护
  • 易于通话(简单的通话约定)

tasks.gs

class TasksNamespace {
  constructor() {
    this.webAppDevUrl = 'https://script.google.com/macros/s/<your web app's dev id>/dev';
    this.accessToken = ScriptApp.getOAuthToken();
  }

  // send all requests
  all(requests) {
    return requests
    .map(r => ({
      muteHttpExceptions: true,
      url: this.webAppDevUrl,
      method: 'POST',
      contentType: 'application/json',
      payload: {
        functionName: r.first(),
        arguments: r.removeFirst()
      }.toJson(),
      headers: {
        Authorization: 'Bearer ' + this.accessToken
      }
    }), this)
    .fetchAll()
    .map(r => r.getContentText().toObject())
  }

  // send all responses
  process(request) {
    return ContentService
    .createTextOutput(
      request
      .postData
      .contents
      .toObject()
      .using(This => ({
        ...This,
        result: (() => {
          try {
            return eval(This.functionName).apply(eval(This.functionName.splitOffLast()), This.arguments) // this could cause an error
          }
          catch(error) {
            return error;
          }
        })()
      }))
      .toJson()
    )
    .setMimeType(ContentService.MimeType.JSON)
  }
}

helpers.gs

  // array prototype

  Array.prototype.fetchAll = function() {
    return UrlFetchApp.fetchAll(this);
  }

  Array.prototype.first = function() {
    return this[0];
  }

  Array.prototype.removeFirst = function() {
    this.shift();
    return this;
  }

  Array.prototype.removeLast = function() {
    this.pop();
    return this;
  }


  // string prototype

  String.prototype.blankToUndefined = function(search) {
    return this.isBlank() ? undefined : this;
  };    

  String.prototype.isBlank = function() {
    return this.trim().length == 0;
  }

  String.prototype.splitOffLast = function(delimiter = '.') {
    return this.split(delimiter).removeLast().join(delimiter).blankToUndefined();
  }

  // To Object - if string is Json
  String.prototype.toObject = function() {
    if(this.isBlank())
      return {};
    return JSON.parse(this, App.Strings.parseDate);
  }

  // object prototype

  Object.prototype.toJson = function() {
    return JSON.stringify(this);
  }

  Object.prototype.using = function(func) {
    return func.call(this, this);
  }

http.handler.gs

function doPost(request) {
  return new TasksNamespace.process(request);
}

通话约定

只需使数组具有完整的函数名,其余的就是函数的参数。一切完成后它将返回,就像Promise.all()

var a =  new TasksNamespace.all([
    ["App.Data.Firebase.Properties.getById",'T006DB4'],
    ["App.Data.External.CISC.Properties.getById",'T00A21F', true, 12],
    ["App.Maps.geoCode",'T022D62', false]
  ])

返回预览

[ { functionName: 'App.Data.Firebase.Properties.getById',
    arguments: [ 'T006DB4' ],
    result: 
     { Id: '',
       Listings: [Object],
       Pages: [Object],
       TempId: 'T006DB4',
       Workflow: [Object] } },
...
]

注释

  • 它可以处理任何静态方法,根对象树之外的任何方法或任何根(全局)函数。
  • 它可以处理0个或更多(任意数量)的任何类型的参数
  • 它通过从任何帖子中返回错误来处理错误