Skip to content
This repository was archived by the owner on Dec 1, 2024. It is now read-only.

Commit 4b35716

Browse files
committed
Align nextTick behavior with abstract-leveldown
By using `queueMicrotask()` in browsers, for a consistent experience regardless of bundler (and of runtime in the future, when we drop node 10). Webpack no longer shims node core modules. In theory bundlers can now skip shimming `process`, were it not for our `readable-stream` dependency which uses `process.nextTick()` at the time of writing and has more reason to keep doing so. In modules where the risk of timing issues (due to not having a common microtask scheduler between modules) is low, like here in `levelup`, I prefer the idiomatic `queueMicroTask()` function. Lastly, maybe streams are not here to stay. Async iterators are coming to the level ecosystem. Or maybe we'll switch to `streamx` which is planning to use `queueMicrotask()` as well.
1 parent 29d8b5d commit 4b35716

10 files changed

+39
-13
lines changed

lib/levelup.js

+7-4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ const catering = require('catering')
1212
const getCallback = require('./common').getCallback
1313
const getOptions = require('./common').getOptions
1414

15+
// TODO: after we drop node 10, also use queueMicrotask() in node
16+
const nextTick = require('./next-tick')
17+
1518
const WriteError = errors.WriteError
1619
const ReadError = errors.ReadError
1720
const NotFoundError = errors.NotFoundError
@@ -46,7 +49,7 @@ function LevelUP (db, options, callback) {
4649
if (!db || typeof db !== 'object') {
4750
error = new InitializationError('First argument must be an abstract-leveldown compliant store')
4851
if (typeof callback === 'function') {
49-
return process.nextTick(callback, error)
52+
return nextTick(callback, error)
5053
}
5154
throw error
5255
}
@@ -97,7 +100,7 @@ LevelUP.prototype.open = function (opts, callback) {
97100
}
98101

99102
if (this.isOpen()) {
100-
process.nextTick(callback, null, this)
103+
nextTick(callback, null, this)
101104
return callback.promise
102105
}
103106

@@ -132,7 +135,7 @@ LevelUP.prototype.close = function (callback) {
132135
this.emit('closing')
133136
this.db = new DeferredLevelDOWN(this._db)
134137
} else if (this.isClosed()) {
135-
process.nextTick(callback)
138+
nextTick(callback)
136139
} else if (this.db.status === 'closing') {
137140
this.once('closed', callback)
138141
} else if (this._isOpening()) {
@@ -299,7 +302,7 @@ LevelUP.prototype.type = 'levelup'
299302

300303
function maybeError (db, callback) {
301304
if (!db._isOpening() && !db.isOpen()) {
302-
process.nextTick(callback, new ReadError('Database is not open'))
305+
nextTick(callback, new ReadError('Database is not open'))
303306
return true
304307
}
305308
}

lib/next-tick-browser.js

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
'use strict'
2+
3+
const queueMicrotask = require('queue-microtask')
4+
5+
module.exports = function (fn, ...args) {
6+
if (args.length === 0) {
7+
queueMicrotask(fn)
8+
} else {
9+
queueMicrotask(() => fn(...args))
10+
}
11+
}

lib/next-tick.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
'use strict'
2+
3+
module.exports = process.nextTick

package.json

+6-2
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@
44
"description": "Fast & simple storage - a Node.js-style LevelDB wrapper",
55
"license": "MIT",
66
"main": "lib/levelup.js",
7+
"browser": {
8+
"./lib/next-tick.js": "./lib/next-tick-browser.js"
9+
},
710
"scripts": {
811
"test": "standard && hallmark && (nyc -s node test/self.js | faucet) && nyc report",
912
"coverage": "nyc report --reporter=text-lcov | coveralls",
1013
"test-browsers": "airtap --verbose test/self.js > airtap.log",
1114
"test-browsers-local": "airtap -p local test/self.js",
1215
"hallmark": "hallmark --fix",
13-
"dependency-check": "dependency-check --no-dev .",
16+
"dependency-check": "dependency-check --no-dev -i queue-microtask .",
1417
"prepublishOnly": "npm run dependency-check"
1518
},
1619
"files": [
@@ -25,7 +28,8 @@
2528
"deferred-leveldown": "~5.3.0",
2629
"level-errors": "^3.0.0",
2730
"level-iterator-stream": "^5.0.0",
28-
"level-supports": "^2.0.0"
31+
"level-supports": "^2.0.0",
32+
"queue-microtask": "^1.2.3"
2933
},
3034
"devDependencies": {
3135
"after": "^0.8.2",

test/create-stream-vs-put-racecondition.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const levelup = require('../lib/levelup')
2+
const nextTick = require('../lib/next-tick')
23
const memdown = require('memdown')
34
const encdown = require('encoding-down')
45
const after = require('after')
@@ -23,7 +24,7 @@ function makeTest (test, encode, deferredOpen, delayedPut) {
2324

2425
test(name, function (t) {
2526
const db = encode ? levelup(encdown(memdown())) : levelup(memdown())
26-
const delay = delayedPut ? process.nextTick : callFn
27+
const delay = delayedPut ? nextTick : callFn
2728

2829
run(t, db, !deferredOpen, delay)
2930
})

test/idempotent-test.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const levelup = require('../lib/levelup.js')
2+
const nextTick = require('../lib/next-tick')
23
const memdown = require('memdown')
34
const sinon = require('sinon')
45

@@ -18,7 +19,7 @@ module.exports = function (test, testCommon) {
1819

1920
// close needs to be idempotent too.
2021
db.close()
21-
process.nextTick(db.close.bind(db))
22+
nextTick(db.close.bind(db))
2223
}
2324

2425
const db = levelup(memdown(), function () {

test/init-test.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const levelup = require('../lib/levelup')
2+
const nextTick = require('../lib/next-tick')
23
const memdown = require('memdown')
34

45
module.exports = function (test, testCommon) {
@@ -51,7 +52,7 @@ module.exports = function (test, testCommon) {
5152

5253
const mem = memdown()
5354
mem._open = function (opts, cb) {
54-
process.nextTick(cb, new Error('from underlying store'))
55+
nextTick(cb, new Error('from underlying store'))
5556
}
5657

5758
levelup(mem, function (err) {
@@ -68,7 +69,7 @@ module.exports = function (test, testCommon) {
6869

6970
const mem = memdown()
7071
mem._open = function (opts, cb) {
71-
process.nextTick(cb, new Error('from underlying store'))
72+
nextTick(cb, new Error('from underlying store'))
7273
}
7374

7475
levelup(mem)

test/read-stream-test.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const bigBlob = Array.apply(null, Array(1024 * 100)).map(function () { return 'a
33
const discardable = require('./util/discardable')
44
const readStreamContext = require('./util/rs-context')
55
const rsFactory = require('./util/rs-factory')
6+
const nextTick = require('../lib/next-tick')
67

78
module.exports = function (test, testCommon) {
89
const createReadStream = rsFactory(testCommon)
@@ -523,7 +524,7 @@ module.exports = function (test, testCommon) {
523524
const rs = createReadStream(db)
524525
.on('close', done)
525526

526-
process.nextTick(function () {
527+
nextTick(function () {
527528
rs.destroy()
528529
})
529530
})

test/self.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,6 @@ suite({
5050
// Integration tests that can't use a generic testCommon.factory()
5151
require('./self/manifest-test')
5252

53-
if (!process.browser) {
53+
if (typeof process === 'undefined' || !process.browser) {
5454
require('./browserify-test')(test)
5555
}

test/snapshot-test.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const trickle = require('trickle')
33
const discardable = require('./util/discardable')
44
const readStreamContext = require('./util/rs-context')
55
const rsFactory = require('./util/rs-factory')
6+
const nextTick = require('../lib/next-tick')
67

78
module.exports = function (test, testCommon) {
89
const createReadStream = rsFactory(testCommon)
@@ -28,7 +29,7 @@ module.exports = function (test, testCommon) {
2829
done()
2930
}, 0.05))
3031

31-
process.nextTick(function () {
32+
nextTick(function () {
3233
// 3) Concoct and write new random data over the top of existing items.
3334
// If we're not using a snapshot then then we'd expect the test
3435
// to fail because it'll pick up these new values rather than the

0 commit comments

Comments
 (0)