Skip to content

Commit 3ee9289

Browse files
authored
Add async iterator (#10)
* Add async iterator to LevelDB and Iterator * lint
1 parent 9c4cbed commit 3ee9289

10 files changed

+70
-30
lines changed

HISTORY.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## 1.2.0
2+
* Add async iterator support for LevelDB and Iterator, use with `for await`
3+
14
## 1.1.1
25
* Fix iterators not return all values [#8](https://github.com/extremeheat/node-leveldb-zlib/issues/8), [#9](https://github.com/extremeheat/node-leveldb-zlib/pull/9)
36

binding.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,17 @@ if (!process.versions.electron) {
66
// Electron has its own crash handler, and segfault-handler
77
// uses NAN which is a hassle, so only load outside electron
88
try {
9-
var SegfaultHandler = require('segfault-handler')
9+
const SegfaultHandler = require('segfault-handler')
1010
SegfaultHandler.registerHandler('crash.log')
1111
} catch (e) {
1212
debug('[leveldb] segfault handler is not installed. If you run into crashing issues, install it with `npm i -D segfault-handler` to get debug info on native crashes')
1313
}
1414
}
1515

16-
var bindings
17-
var pathToSearch = helper.getPath()
16+
let bindings
17+
const pathToSearch = helper.getPath()
1818
if (pathToSearch) {
19-
var rpath = path.join(__dirname, pathToSearch, '/node-leveldb.node')
19+
const rpath = path.join(__dirname, pathToSearch, '/node-leveldb.node')
2020
try {
2121
bindings = require(rpath)
2222
} catch (e) {

buildChecks.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ function checkIfPrebuildExists () {
1414
}
1515
}
1616

17-
var runCmake = true
17+
let runCmake = true
1818

1919
if (!process.env.FORCE_BUILD) {
2020
if (checkIfPrebuildExists()) {

docs/API.md

+2-5
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,10 @@ The keys and values can be either Buffers or Strings: strings will be converted
88
const db = new LevelDB(pathToDb, { createIfMissing: true })
99
await db.open() // Make sure to wait for DB to open!
1010
await db.put('Hello', 'World')
11-
const iter = db.getIterator({ values: true, keys: true }) // Both `keys` and `values` default to true
12-
let entry
13-
while (entry = await iter.next()) {
14-
const [ val, key ] = entry.map(k => String(k))
11+
for await (const [key, val] = await db.getIterator({ keyAsBuffer: false, valueAsBuffer: false })) {
1512
console.log('Read', key, val)
1613
}
17-
await db.close() // Make sure to save and close when you're done!
14+
await db.close() // Make sure to save and close when you're done!
1815
```
1916

2017
See [example.js](./example.js) for an simple example program.

docs/example.js

+1-4
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,7 @@ async function basicTest (pathToDb) {
1212
async function iterate (pathToDb) {
1313
const db = new LevelDB(pathToDb)
1414
await db.open() // Make sure to wait for DB to open!
15-
const iter = db.getIterator({ values: true, keys: true }) // Both `keys` and `values` default to true
16-
let entry
17-
while (entry = await iter.next()) {
18-
const [val, key] = entry.map(k => String(k))
15+
for await (const [key, val] of db.getIterator({ keyAsBuffer: false, valueAsBuffer: false })) {
1916
console.log('Read', key, val)
2017
}
2118
await db.close() // Make sure to save and close when you're done!

package.json

+9-9
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"build": "tsc",
1111
"postci": "node helpers/postCI.js",
1212
"prepublish": "cd helpers && node npmPublish.js",
13+
"fix": "ts-standard --fix",
1314
"test": "npm run build && mocha"
1415
},
1516
"author": "extremeheat",
@@ -18,21 +19,20 @@
1819
"type": "git",
1920
"url": "git://github.com/extremeheat/node-leveldb-zlib.git"
2021
},
21-
"devDependencies": {
22-
"@types/node": "^14.14.16",
23-
"leveldb-zlib": "file:.",
24-
"segfault-handler": "^1.3.0",
25-
"ts-node": "^9.1.1",
26-
"ts-standard": "^10.0.0",
27-
"typescript": "^4.1.3"
28-
},
2922
"dependencies": {
3023
"bindings": "^1.5.0",
3124
"cmake-js": "^6.1.0",
3225
"debug": "^4.3.1",
33-
"mocha": "^8.4.0",
3426
"node-addon-api": "^3.1.0"
3527
},
28+
"devDependencies": {
29+
"@types/node": "^14.14.16",
30+
"leveldb-zlib": "file:.",
31+
"ts-node": "^9.1.1",
32+
"ts-standard": "^10.0.0",
33+
"typescript": "^4.1.3",
34+
"mocha": "^8.4.0"
35+
},
3636
"binary": {
3737
"napi_versions": [
3838
3

test/extra.test.js

+25
Original file line numberDiff line numberDiff line change
@@ -102,4 +102,29 @@ it('can iterate the db', async function () {
102102
assert.ok(val === 'Value')
103103
}
104104
assert.strictEqual(i, 3)
105+
await db.close()
106+
})
107+
108+
it('can iterate with asyncIterator', async function () {
109+
try { fs.rmSync('./db', { recursive: true }) } catch {}
110+
const db = new LevelDB('./db', { createIfMissing: true })
111+
await db.open()
112+
const keys = ['Key1', 'Key2', 'Key3']
113+
114+
for (const key of keys) {
115+
await db.put(key, 'Value')
116+
}
117+
118+
for await (const [key, val] of db) {
119+
continue
120+
}
121+
122+
let i = 0
123+
for await (const [key, val] of db.getIterator({ keyAsBuffer: false, valueAsBuffer: false })) {
124+
assert.strictEqual(key, keys[i++])
125+
assert.strictEqual(val, 'Value')
126+
}
127+
128+
assert.strictEqual(i, 3)
129+
await db.close()
105130
})

test/main.test.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ it('create and write new db', async () => {
1212
await db.put('Hello', 'World!')
1313
await db.put('I', 'Like')
1414
const f32a = new Float32Array(10)
15-
for (var i = 0; i < 10; i++) {
15+
for (let i = 0; i < 10; i++) {
1616
f32a[i] = (3.14159 * Math.random())
1717
}
1818
await db.put('Pi', Buffer.from(f32a.buffer))
@@ -55,7 +55,7 @@ it('random read/write x10', async function () {
5555
}
5656

5757
const promises = []
58-
for (var i = 0; i < 10; i++) {
58+
for (let i = 0; i < 10; i++) {
5959
promises.push(runTest(i, i % 2 === 0))
6060
}
6161
await Promise.all(promises)
@@ -65,7 +65,7 @@ it('random read/write x10', async function () {
6565
it('minecraft', async () => {
6666
const path = join(__dirname, './mctestdb')
6767

68-
var Tag = {}
68+
const Tag = {}
6969
Tag[Tag.VersionNew = 44] = 'VersionNew'
7070
Tag[Tag.Data2D = 45] = 'Data2D'
7171
Tag[Tag.Data2DLegacy = 46] = 'Data2DLegacy'

ts/iterator.ts

+13-4
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export class Iterator {
4343
if (this.finished) return null
4444
await (this.#lock = this._next())
4545
this.#lock = null
46-
return this.cache.length ? this.cache.splice(-2, 2) : null
46+
return (this.cache.length > 0) ? this.cache.splice(-2, 2) : null
4747
}
4848

4949
async end () {
@@ -53,6 +53,15 @@ export class Iterator {
5353
})
5454
}
5555

56+
async * [Symbol.asyncIterator] () {
57+
let next
58+
while (next = await this.next()) {
59+
const [value, key] = next
60+
yield [key, value]
61+
}
62+
this.end()
63+
}
64+
5665
//
5766

5867
static readonly rangeOptions = 'start end gt gte lt lte'.split(' ')
@@ -62,12 +71,12 @@ export class Iterator {
6271
}
6372

6473
static cleanRangeOptions (options) {
65-
var result = {}
74+
const result = {}
6675

67-
for (var k in options) {
76+
for (const k in options) {
6877
if (!Object.prototype.hasOwnProperty.call(options, k)) continue
6978

70-
var opt = options[k]
79+
let opt = options[k]
7180

7281
if (this.isRangeOption(k)) {
7382
// Note that we don't reject nullish and empty options here. While

ts/leveldb.ts

+9
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,15 @@ export class LevelDB {
208208
return new Iterator(this, options)
209209
}
210210

211+
async * [Symbol.asyncIterator] () {
212+
const it = this.getIterator()
213+
let next
214+
while (next = await it.next()) {
215+
const [value, key] = next
216+
yield [key, value]
217+
}
218+
}
219+
211220
/**
212221
* Delete all entries or a range.
213222
*/

0 commit comments

Comments
 (0)