Skip to content

Commit eedeed9

Browse files
authored
Implement Symbol.asyncDispose (#95)
Per https://github.com/tc39/proposal-explicit-resource-management which in TypeScript (5.2) allows you to skip calling `.close()`: ```ts await db.put('example', 'before') await using snapshot = db.snapshot() await db.put('example', 'after') await db.get('example', { snapshot })) // Returns 'before' ``` Works on databases, iterators, snapshots and batches. Category: addition
1 parent f81d348 commit eedeed9

13 files changed

+126
-4
lines changed

README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,33 @@ const xyz = db.sublevel<string, any>('xyz', { valueEncoding: 'json' })
6161

6262
</details>
6363

64+
TypeScript users can benefit from the `using` keyword because `abstract-level` implements [`Symbol.asyncDispose`](https://github.com/tc39/proposal-explicit-resource-management) on its resources. For example:
65+
66+
<details><summary>Using example</summary>
67+
68+
```ts
69+
await db.put('example', 'before')
70+
await using snapshot = db.snapshot()
71+
await db.put('example', 'after')
72+
await db.get('example', { snapshot })) // Returns 'before'
73+
```
74+
75+
The equivalent in JavaScript would be:
76+
77+
```js
78+
await db.put('example', 'before')
79+
const snapshot = db.snapshot()
80+
81+
try {
82+
await db.put('example', 'after')
83+
await db.get('example', { snapshot })) // Returns 'before'
84+
} finally {
85+
await snapshot.close()
86+
}
87+
```
88+
89+
</details>
90+
6491
## Install
6592

6693
With [npm](https://npmjs.org) do:

abstract-chained-batch.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,12 @@ class AbstractChainedBatch {
358358
async _close () {}
359359
}
360360

361+
if (typeof Symbol.asyncDispose === 'symbol') {
362+
AbstractChainedBatch.prototype[Symbol.asyncDispose] = async function () {
363+
return this.close()
364+
}
365+
}
366+
361367
const prepareClose = function (batch) {
362368
let close
363369

abstract-iterator.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,12 @@ class CommonIterator {
274274
}
275275
}
276276

277+
if (typeof Symbol.asyncDispose === 'symbol') {
278+
CommonIterator.prototype[Symbol.asyncDispose] = async function () {
279+
return this.close()
280+
}
281+
}
282+
277283
// For backwards compatibility this class is not (yet) called AbstractEntryIterator.
278284
class AbstractIterator extends CommonIterator {
279285
constructor (db, options) {

abstract-level.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -945,6 +945,12 @@ const { AbstractSublevel } = require('./lib/abstract-sublevel')({ AbstractLevel
945945
exports.AbstractLevel = AbstractLevel
946946
exports.AbstractSublevel = AbstractSublevel
947947

948+
if (typeof Symbol.asyncDispose === 'symbol') {
949+
AbstractLevel.prototype[Symbol.asyncDispose] = async function () {
950+
return this.close()
951+
}
952+
}
953+
948954
const assertOpen = function (db) {
949955
if (db[kStatus] !== 'open') {
950956
throw new ModuleError('Database is not open', {

abstract-snapshot.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@ class AbstractSnapshot {
7070
async _close () {}
7171
}
7272

73+
if (typeof Symbol.asyncDispose === 'symbol') {
74+
AbstractSnapshot.prototype[Symbol.asyncDispose] = async function () {
75+
return this.close()
76+
}
77+
}
78+
7379
const privateClose = async function (snapshot, owner) {
7480
await snapshot._close()
7581
owner.detachResource(snapshot)

test/chained-batch-test.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,10 +308,24 @@ exports.tearDown = function (test, testCommon) {
308308
})
309309
}
310310

311+
exports.dispose = function (test, testCommon) {
312+
// Can't use the syntax yet (https://github.com/tc39/proposal-explicit-resource-management)
313+
Symbol.asyncDispose && test('Symbol.asyncDispose', async function (t) {
314+
const db = testCommon.factory()
315+
await db.open()
316+
317+
const batch = db.batch()
318+
await batch[Symbol.asyncDispose]()
319+
320+
return db.close()
321+
})
322+
}
323+
311324
exports.all = function (test, testCommon) {
312325
exports.setUp(test, testCommon)
313326
exports.args(test, testCommon)
314327
exports.batch(test, testCommon)
315328
exports.events(test, testCommon)
316329
exports.tearDown(test, testCommon)
330+
exports.dispose(test, testCommon)
317331
}

test/iterator-explicit-snapshot-test.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,13 +303,27 @@ exports.cleanup = function (test, testCommon) {
303303
})
304304
}
305305

306+
exports.dispose = function (test, testCommon) {
307+
// Can't use the syntax yet (https://github.com/tc39/proposal-explicit-resource-management)
308+
Symbol.asyncDispose && test('Symbol.asyncDispose', async function (t) {
309+
const db = testCommon.factory()
310+
await db.open()
311+
312+
const snapshot = db.snapshot()
313+
await snapshot[Symbol.asyncDispose]()
314+
315+
return db.close()
316+
})
317+
}
318+
306319
exports.all = function (test, testCommon) {
307320
exports.traits(test, testCommon)
308321
exports.get(test, testCommon)
309322
exports.getMany(test, testCommon)
310323
exports.iterator(test, testCommon)
311324
exports.clear(test, testCommon)
312325
exports.cleanup(test, testCommon)
326+
exports.dispose(test, testCommon)
313327
}
314328

315329
function testFactory (test, testCommon) {

test/iterator-test.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,11 +597,25 @@ exports.tearDown = function (test, testCommon) {
597597
})
598598
}
599599

600+
exports.dispose = function (test, testCommon) {
601+
// Can't use the syntax yet (https://github.com/tc39/proposal-explicit-resource-management)
602+
Symbol.asyncDispose && test('Symbol.asyncDispose', async function (t) {
603+
const db = testCommon.factory()
604+
await db.open()
605+
606+
const iterator = db.iterator()
607+
await iterator[Symbol.asyncDispose]()
608+
609+
return db.close()
610+
})
611+
}
612+
600613
exports.all = function (test, testCommon) {
601614
exports.setUp(test, testCommon)
602615
exports.args(test, testCommon)
603616
exports.sequence(test, testCommon)
604617
exports.iterator(test, testCommon)
605618
exports.decode(test, testCommon)
606619
exports.tearDown(test, testCommon)
620+
exports.dispose(test, testCommon)
607621
}

test/open-test.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,14 @@ exports.open = function (test, testCommon) {
248248
await new Promise((resolve) => db.once('open', resolve))
249249
return db.close()
250250
})
251+
252+
// Can't use the syntax yet (https://github.com/tc39/proposal-explicit-resource-management)
253+
Symbol.asyncDispose && test('Symbol.asyncDispose', async function (t) {
254+
const db = testCommon.factory()
255+
await db.open()
256+
await db[Symbol.asyncDispose]()
257+
t.is(db.status, 'closed')
258+
})
251259
}
252260

253261
exports.all = function (test, testCommon) {

types/abstract-chained-batch.d.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import * as Transcoder from 'level-transcoder'
22
import { AbstractSublevel } from './abstract-sublevel'
33

4-
export class AbstractChainedBatch<TDatabase, KDefault, VDefault> {
4+
export class AbstractChainedBatch<TDatabase, KDefault, VDefault>
5+
implements AsyncDisposable {
56
constructor (db: TDatabase)
67

78
/**
@@ -53,6 +54,11 @@ export class AbstractChainedBatch<TDatabase, KDefault, VDefault> {
5354
* committing it.
5455
*/
5556
close (): Promise<void>
57+
58+
/**
59+
* Close the batch.
60+
*/
61+
[Symbol.asyncDispose](): Promise<void>
5662
}
5763

5864
/**

0 commit comments

Comments
 (0)