diff --git a/README.md b/README.md index 3e73281..f353bb8 100644 --- a/README.md +++ b/README.md @@ -570,6 +570,8 @@ setTimeout(() => query.cancel(), 100) const result = await query ``` +`query.cancel()` returns a promise that settles when the cancel request is done. It rejects if the request could not be sent, for example when a connection for it cannot be opened, so handle it to avoid an unhandled rejection. Calling it again returns the same promise. + ### Execute #### ```await sql``.execute()``` diff --git a/src/query.js b/src/query.js index 0d44a15..0227cc8 100644 --- a/src/query.js +++ b/src/query.js @@ -50,7 +50,7 @@ export class Query extends Promise { } cancel() { - return this.canceller && (this.canceller(this), this.canceller = null) + return this.cancelling || (this.cancelling = this.canceller && this.canceller(this)) } simple() { diff --git a/tests/index.js b/tests/index.js index 845c6ce..d7f921e 100644 --- a/tests/index.js +++ b/tests/index.js @@ -2302,6 +2302,31 @@ t('Cancel queued query', async() => { return ['57014', error.code] }) +t('Cancel returns a promise that rejects when the cancel request fails', async() => { + let sockets = 0 + const sql = postgres({ + ...options, + socket: () => { + if (sockets++) + throw new Error('no socket for the cancel request') + return new Promise((resolve, reject) => { + const socket = new net.Socket() + socket.connect(5432) + socket.on('error', reject) + socket.on('connect', () => resolve(socket)) + }) + } + }) + await sql`select 1` + const query = sql`select pg_sleep(0.2)`.execute() + await delay(50) + const cancelled = query.cancel() + const rejected = await cancelled.then(() => false, e => e instanceof Error) + await query + await sql.end() + return ['true,true', [rejected, query.cancel() === cancelled].join()] +}) + t('Fragments', async() => [ 1, (await sql` diff --git a/types/index.d.ts b/types/index.d.ts index 13c3432..6103ccd 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -605,7 +605,11 @@ declare namespace postgres { writable(): Promise; execute(): this; - cancel(): void; + /** + * Sends a cancel request for this query, once, however often it is called + * @returns A promise that settles when the cancel request is done, and rejects if it could not be sent + */ + cancel(): Promise; /** * @deprecated `.stream` has been renamed to `.forEach`