PGlite 如何用 .listen() 与 NOTIFY 实现数据库内频道通知?
2026/9/14 9:01:35 网站建设 项目流程

PGlite 如何用 .listen() 与 NOTIFY 实现数据库内频道通知?

【免费下载链接】pgliteEmbeddable Postgres with real-time, reactive bindings.项目地址: https://gitcode.com/GitHub_Trending/pg/pglite

在 PGlite 中实现「某个事件发生后,JavaScript 端立刻收到一条消息」这类需求,不需要自己轮询数据表:Postgres 原生的LISTEN/NOTIFY机制在 PGlite 里被封装成了.listen()方法。执行NOTIFY语句后,订阅了对应频道的回调函数会收到 payload 字符串。这套 API 在 Node、Bun、Deno 和浏览器中都可用(PGlite 同时支持这两个运行环境),本文走一遍从安装、订阅频道、发送通知到取消订阅的完整路径,以及如何验证通知确实到达了回调。

准备条件:安装并创建实例

先用包管理器安装 PGlite(以 npm 为例,其他包管理器见 Getting started):

npm install @electric-sql/pglite

创建实例。不带参数时是内存数据库,适合演示和开发:

import { PGlite } from '@electric-sql/pglite' const pg = new PGlite()

如果需要持久化,dataDir支持不同存储后端(详见 PGlite API 文档):

  • file://或不加前缀:文件系统存储,Node 和 Bun 可用;
  • idb://:IndexedDB 存储,浏览器可用;
  • memory://:内存临时存储,全平台可用。

例如浏览器中持久化到 IndexedDB:new PGlite('idb://my-pgdata')

也可以改用静态方法await PGlite.create(options),它会额外等待.waitReady的 Promise,确保数据库完全初始化后再返回实例。不过查询方法本身在数据库未就绪时会自动等待waitReady完成,所以一般不必手动等待。

主路径:用 .listen() 订阅频道并接收 NOTIFY

.listen()的签名(来自 API 文档):

.listen(channel: string, callback: (payload: string) => void): Promise<() => Promise<void>>

它订阅一个pg_notify频道,回调接收通知的 payload(字符串),并返回一个取消订阅函数。.listen()会自动处理订阅,这一点与后文的onNotification不同。

一条最短的完整示例,可以直接在 Node 中运行:

import { PGlite } from '@electric-sql/pglite' const pg = new PGlite() // 订阅频道 'test',回调接收 payload const unsub = await pg.listen('test', (payload) => { console.log('Received:', payload) }) // 向频道 'test' 发送一条带 payload 的通知 await pg.query("NOTIFY test, 'Hello, world!'") // 确认收到通知后取消订阅 await unsub() // 干净地关闭数据库 await pg.close()

回调是异步被触发的:NOTIFY语句执行完成后,通知会经由 Postgres 通知机制到达回调,而不是同步在query返回的同时调用。官方浏览器示例 notify.html 因此在每步操作之间显式等待了 500ms,用于让日志按顺序展示。

官方浏览器示例的完整流程值得注意最后一步:

await unsub() await pg.query("NOTIFY test, 'Will not be received!'")

取消订阅后再NOTIFY,这条通知不会到达回调——这是判断取消订阅是否生效的直接依据。

验证通知是否到达

三个层级的验证方式都在仓库里:

  1. 控制台输出:示例中的console.log('Received:', payload)会打印收到的 payload,文档示例输出为Received: Hello, world!(文档示例,实际 payload 取决于你NOTIFY传入的内容)。
  2. 官方测试断言:notify.test.ts 中的notify用例订阅频道后执行await pg.exec("NOTIFY test, '321'"),并断言回调收到的payload严格等于'321'unlisten用例则断言取消订阅后再NOTIFY不会触发回调。
  3. 浏览器示例页面:examples 列表 中的 "Notify and Listen" 对应 packages/pglite/examples/notify.html,页面会输出收到的消息日志,可以直观看到订阅期内收到两条消息、取消订阅后的那条没有到达。

取消订阅的两种方式

  • listen返回的 unsubscribe 函数await unsub()只移除当前这个回调。
  • .unlisten(channel, callback?):提供callback时只移除该回调;不提供时移除该频道上的所有回调。签名:.unlisten(channel: string, callback?: (payload: string) => void): Promise<void>

一个回调可能多次调用.listen()订阅同一频道(每次调用都会追加一个订阅),此时用带callback参数的.unlisten()精确移除,而不是清掉整个频道。

替代路径:onNotification 接收所有频道的通知

如果不想按频道逐个注册回调,可以用onNotification挂一个全局处理器,它会收到 Postgres 发来的所有通知,回调签名是(channel: string, payload: string) => void

pg.onNotification((channel, payload) => { console.log(channel, payload) }) await pg.exec('LISTEN test') await pg.exec("NOTIFY test, '123'")

API 文档明确警告:onNotification只添加事件处理器,并不执行订阅,必须自己先用LISTEN channel_name订阅频道,通知才会到达。这与.listen()(内部自动完成订阅)是主要区别。不再需要时用offNotification(callback)移除对应处理器。

频道命名规则与易错点

.listen()的频道名遵循 Postgres 的标识符规则,这也是官方测试专门覆盖的行为(见 notify.test.ts 的 "check notify case sensitivity + special chars as Postgresql" 用例):

  • 不加双引号时频道名会被转为小写pg.listen('TeStiNG')等价于pg.listen('testing')NOTIFY PostgresDefaultLowerNOTIFY postgresdefaultlower命中的是同一频道。
  • 加双引号时区分大小写pg.listen('"TeST"')pg.listen('test')是不同频道;大小写不同的双引号频道名之间不会互相收到通知。
  • 含空格或特殊字符的频道名必须加双引号pg.listen('"Quoted Channel With Spaces"')pg.listen('"test&me"')可以正常订阅;而pg.listen('Unquoted Channel With Spaces')pg.listen('test&me')(不带引号)会直接抛错,对应的NOTIFY语句同样会失败。

如果你的业务里频道名来自变量,建议统一用双引号包裹并保证LISTEN/NOTIFY两端引号用法一致,避免因为隐式小写化或非法字符导致「发了通知但收不到」。

小结与延伸

  • 主路径是.listen(channel, callback)订阅 +NOTIFY channel, 'payload'发送,取消订阅用返回的unsub().unlisten();验证依据是回调收到的 payload 与你NOTIFY的内容一致、取消订阅后不再收到。
  • 需要跨频道统一处理时用onNotification,但记得它不替你LISTEN
  • 更完整的接口说明见 PGlite API 文档,可直接运行的浏览器示例见 packages/pglite/examples/notify.html,行为断言见 packages/pglite/tests/notify.test.ts。

【免费下载链接】pgliteEmbeddable Postgres with real-time, reactive bindings.项目地址: https://gitcode.com/GitHub_Trending/pg/pglite

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询