您可以将UrlFetchApp转换为Apps脚本中的Promise吗?

问题描述

UrlFetchApp.fetch同步执行,使执行速度变慢。是否可以将UrlFetchApp转换为Promise?

我一直在想这种方法

  1. 返回HTTPResponse.getContent()作为承诺,并将所有网址添加到队列中。
  2. 将其执行推迟到调用getContent().then()之前。
  3. 调用任意一个网址中的getContent()时,请使用fetchAll获取所有网址 结果并清除队列。

您认为这种方法有什么问题吗?

解决方法

从理论上讲,这种方法似乎是合理的,尤其是因为已知.fetchAll executes asynchronously

  • .fetch()调用是实际获取的地方。因此,在进行fetch调用之前,应插入UrlFetchApp对象的所有钩子。

  • 您可以使用Proxy object来挂接.fetch上的UrlFetchApp调用,以返回带有HTTPResponse对象的伪thenable

  • 然后按照问题所述在fetchAll通话中使用.getContent

  • 但是请注意,应用程序脚本中的promise可能会或可能不会按照issue comments #1 to #4中的说明异步执行。但是,这不应该与您的方法有关。

鉴于承诺的挑剔性质和不明确的文档,最好在任何生产环境中避免使用它们。实现批处理请求的一种更好的方法是对thenable对象使用纯自定义函数:

function test() {
  /**
   * @description Batches requests until then is called on a response
   *   and fetches all batched requests
   * @return {object} A then object,which when called fetches the batch
   * @param {string} url Url to fetch
   * @param {object} options Options to fetch. See UrlFetchApp.fetch
   */
  const fetchAsBatch = function fetch(requests,url,options) {
    options.url = url;
    requests.add(options);
    return {
      then: func => {
        const responses = func(UrlFetchApp.fetchAll([...requests]));
        requests.clear();// clear current batch
        return responses;
      },};
  }.bind(this,new Set());

  const successHandlerLogger = responses => {
    /*Do something with all responses*/
    console.log(responses.map(response => response.getContentText()));
  };
  fetchAsBatch('https://example.com',{ method: 'get' });
  fetchAsBatch('https://httpbin.org/post',{ method: 'post' }).then(
    successHandlerLogger
  );
  fetchAsBatch('https://google.com',{}).then(successHandlerLogger);
}

function test() {
  /**
   * @description Batches requests until then is called on a response
   *   and fetches all batched requests
   * @return {object} A then object,options) {
    options.url = url;
    requests.add(options);
    return {
      then: func => {
        const responses = func(UrlFetchApp.fetchAll([...requests]));
        requests.clear();
        return responses;
      },{}).then(successHandlerLogger);
}
/*Mock urlfetchapp library to return requests without fetch*/
const UrlFetchApp = {
  fetchAll: requests =>
    requests.map(request => ({
      getContentText: () => request,})),};

test();