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

Can you convert UrlFetchApp into a promise in Apps Script?

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

我一直在想这个方法:

  1. Return HTTPResponse.getContent() 作为 Promise 并将所有 url 添加到队列中。
  2. 推迟执行直到 getContent().then() 被调用。
  3. 当调用任意一个url的getContent()时,使用fetchAll获取所有 结果并清除队列。

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

从理论上讲,该方法似乎很合理,尤其是因为众所周知 .fetchAll executes asynchronously

  • .fetch() 调用是实际获取发生的地方。因此,应在 fetch 调用之前插入 UrlFetchApp 对象的任何挂钩。

  • 您可以使用 Proxy object 挂钩 .fetchUrlFetchApp 的调用 return 带有 thenable对象

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

  • 但是请注意,应用程序脚本中的承诺可能会也可能不会异步执行,如 issue comments #1 to #4 中所述。然而,这不应该成为您方法的问题。

鉴于 promise 的挑剔性质和不明确的文档,最好在任何生产环境中避免使用它们。实现批处理请求的更好方法是使用带有 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, 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();
        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);
}
/*Mock urlfetchapp library to return requests without fetch*/
const UrlFetchApp = {
  fetchAll: requests =>
    requests.map(request => ({
      getContentText: () => request,
    })),
};

test();