异步监听器

Antora按照注册顺序同步调用监听器。即使使用async关键字标记监听器或监听器返回一个Promise,这仍然成立。Antora将等待监听器调用完成后再调用下一个监听器(因此在继续自身操作之前)。这种行为与Node.js中内置的NodeEmitter的行为不同。

将监听器标记为async或返回Promise的好处在于,监听器可以执行异步操作。当然,这些操作将在Antora继续之前全部解决,因此它们被设计为在函数边界之外以同步方式运行。

Promise在程序调用await时结束。显然,这个要求一直冒泡到程序的顶层函数。Antora通过允许您将扩展监听器定义为async或返回Promise来隐藏这个细节。

让我们看一个从URL获取文件并发布到站点的示例。

示例1. fetch-and-publish-readme-extension.js
module.exports.register = function () {
  this.on('beforePublish', async ({ siteCatalog }) => {
    const https = require('https')
    const contents = await new Promise((resolve, reject) => {
      const buffer = []
      https
        .get('https://gitlab.com/antora/antora/-/raw/HEAD/README.adoc', (response) => {
          response.on('data', (chunk) => buffer.push(chunk.toString()))
          response.on('end', () => resolve(buffer.join('').trimRight()))
        })
        .on('error', reject)
    })
    siteCatalog.addFile({ contents: Buffer.from(contents), out: { path: 'README.adoc' } })
  })
}

请注意,我们在监听器函数中添加了async关键字。这使我们可以在函数内部使用await关键字。

作为练习,您可以尝试从每个内容源的每个分支检索文件并将其添加到发布站点。为了给您一些提示,您需要访问playbook变量以获取内容源列表。

如果您不希望Antora等待您的异步监听器完成,您可以返回一个空的Promise(例如,return Promise.resolve()),或者您可以从监听器中删除async关键字。但是,如果这样做,您需要添加一个监听器,监听稍后在生成器中发出的事件,例如contextClosed,以便在Antora完成之前解决Promise。

让我们看一个与之前相同的示例,不同之处在于它在生成站点时后台下载README.adoc。为了帮助管理挂起Promise的状态,它还被重写为基于类的扩展

示例2. background-fetch-and-publish-readme-extension.js
const https = require('https')

class FetchAndPublishReadmeExtension {
  static register ({ config }) {
    return new FetchAndPublishReadmeExtension(this, config)
  }

  constructor (context, config) {
    ;(this.context = context)
      .on('playbookBuilt', this.onPlaybookBuilt.bind(this))
      .on('beforePublish', this.onBeforePublish.bind(this))
    this.readmeUrl = config.readmeUrl || 'https://gitlab.com/antora/antora/-/raw/HEAD/README.adoc'
    this.contentsPromise = undefined
  }

  playbookBuilt ({ siteCatalog }) {
    this.contentsPromise = new Promise((resolve, reject) => {
      const buffer = []
      https
        .get(this.readmeUrl, (response) => {
          response.on('data', (chunk) => buffer.push(chunk.toString()))
          response.on('end', () => resolve(buffer.join('').trimRight()))
        })
        .on('error', reject)
    })
  }

  async onBeforePublish ({ siteCatalog }) {
    const contents = await this.contentsPromise
    siteCatalog.addFile({ contents: Buffer.from(contents), out: { path: 'README.adoc' } })
  }
}

module.exports = FetchAndPublishReadmeExtension

请注意,只有onBeforePublish监听器函数是async的,因此它可以等待由onPlaybookBuilt监听器函数启动的Promise。该扩展现在还接受README的URL作为名为readme_url的配置键。