Skip to content

Commit 2935180

Browse files
committed
Add hidden abortOnClose option to iterators
Will be used by `many-level`.
1 parent eb08363 commit 2935180

File tree

2 files changed

+32
-11
lines changed

2 files changed

+32
-11
lines changed

abstract-iterator.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@ const kClosed = Symbol('closed')
1818
const kCloseCallbacks = Symbol('closeCallbacks')
1919
const kKeyEncoding = Symbol('keyEncoding')
2020
const kValueEncoding = Symbol('valueEncoding')
21+
const kAbortOnClose = Symbol('abortOnClose')
2122
const kLegacy = Symbol('legacy')
2223
const kKeys = Symbol('keys')
2324
const kValues = Symbol('values')
2425
const kLimit = Symbol('limit')
2526
const kCount = Symbol('count')
2627

2728
const emptyOptions = Object.freeze({})
29+
const noop = () => {}
2830
let warnedEnd = false
2931

3032
// This class is an internal utility for common functionality between AbstractIterator,
@@ -55,6 +57,12 @@ class CommonIterator {
5557
this[kLimit] = Number.isInteger(options.limit) && options.limit >= 0 ? options.limit : Infinity
5658
this[kCount] = 0
5759

60+
// Undocumented option to abort pending work on close(). Used by the
61+
// many-level module as a temporary solution to a blocked close().
62+
// TODO (next major): consider making this the default behavior. Native
63+
// implementations should have their own logic to safely close iterators.
64+
this[kAbortOnClose] = !!options.abortOnClose
65+
5866
this.db = db
5967
this.db.attachResource(this)
6068
this.nextTick = db.nextTick
@@ -219,6 +227,9 @@ class CommonIterator {
219227
[kFinishWork] () {
220228
const cb = this[kCallback]
221229

230+
// Callback will be null if work was aborted on close
231+
if (this[kAbortOnClose] && cb === null) return noop
232+
222233
this[kWorking] = false
223234
this[kCallback] = null
224235

@@ -277,6 +288,13 @@ class CommonIterator {
277288

278289
if (!this[kWorking]) {
279290
this._close(this[kHandleClose])
291+
} else if (this[kAbortOnClose]) {
292+
// Don't wait for work to finish. Subsequently ignore the result.
293+
const cb = this[kFinishWork]()
294+
295+
cb(new ModuleError('Aborted on iterator close()', {
296+
code: 'LEVEL_ITERATOR_NOT_OPEN'
297+
}))
280298
}
281299
}
282300

test/iterator-test.js

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ exports.sequence = function (test, testCommon) {
127127

128128
// NOTE: adapted from leveldown
129129
test(`${mode}().${method}() after db.close() yields error (deferred: ${deferred})`, async function (t) {
130-
t.plan(1)
130+
t.plan(2)
131131

132132
const db = testCommon.factory()
133133
if (!deferred) await db.open()
@@ -137,22 +137,25 @@ exports.sequence = function (test, testCommon) {
137137

138138
const it = db[mode]()
139139

140-
// The first call should succeed, because it was scheduled before close()
140+
// The first call *should* succeed, because it was scheduled before close(). However, success
141+
// is not a must. Because nextv() and all() fallback to next*(), they're allowed to fail. An
142+
// implementation can also choose to abort any pending call on close.
141143
let promise = it[method](...requiredArgs).then(() => {
142-
// The second call should fail, because it was scheduled after close()
143-
return it[method](...requiredArgs).catch(err => {
144-
t.is(err.code, 'LEVEL_ITERATOR_NOT_OPEN')
145-
})
144+
t.pass('Optionally succeeded')
145+
}).catch((err) => {
146+
t.is(err.code, 'LEVEL_ITERATOR_NOT_OPEN')
146147
})
147148

148-
if (method !== 'next') {
149-
// However, because nextv() and all() fallback to next*(), they're allowed to fail too (for now)
150-
promise = promise.catch((err) => {
149+
// The second call *must* fail, because it was scheduled after close()
150+
promise = promise.then(() => {
151+
return it[method](...requiredArgs).then(() => {
152+
t.fail('Expected an error')
153+
}).catch((err) => {
151154
t.is(err.code, 'LEVEL_ITERATOR_NOT_OPEN')
152155
})
153-
}
156+
})
154157

155-
await Promise.all([db.close(), promise])
158+
return Promise.all([db.close(), promise])
156159
})
157160
}
158161
}

0 commit comments

Comments
 (0)