异步监听器

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

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

当程序调用 await 时,Promise 就结束了。显然,这一要求会一直影响到程序的顶层函数。Antora 允许您将扩展监听器定义为 async 或返回 Promise,从而隐藏了这一细节。

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

Example 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 等待异步监听器的完成,可以返回一个空的承诺(例如,return Promise.resolve()),或者从监听器中移除 async 关键字。不过,如果这样做,就需要添加一个监听器来监听生成器中稍后触发的事件(如 contextClosed),这样就可以在 Antora 完成之前解析承诺。

让我们来看看与前面相同的示例,只不过它是在生成网站的同时在后台下载 README.adoc。为了帮助管理悬而未决的承诺状态,我们还将其重写为 基于类的扩展

Example 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 监听器函数是异步的,因此它可以等待 onPlaybookBuilt 监听器函数启动的承诺。现在,扩展还接受 README 的 URL 作为名为 readme_url 的配置键。