Skip to content

Commit fd802a3

Browse files
charmanderbrianc
authored andcommitted
Don’t create promises when callbacks are provided (#31)
* Revert "When connection fail, emit the error. (#28)" This reverts commit 6a7edab. The callback passed to `Pool.prototype.connect` should be responsible for handling connection errors. The `error` event is documented to be: > Emitted whenever an idle client in the pool encounters an error. This isn’t the case of an idle client in the pool; it never makes it into the pool. It also breaks tests on pg’s master because of nonspecific dependencies. * Don’t create promises when callbacks are provided It’s incorrect to do so. One consequence is that a rejected promise will be unhandled, which is currently annoying, but also dangerous in the future: > DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code. The way callbacks are used currently also causes #24 (hiding of errors thrown synchronously from the callback). One fix for that would be to call them asynchronously from inside the `new Promise()` executor: process.nextTick(cb, error); I don’t think it’s worth implementing, though, since it would still be backwards-incompatible – just less obvious about it. Also fixes a bug where the `Pool.prototype.connect` callback would be called twice if there was an error. * Use Node-0.10-compatible `process.nextTick`
1 parent fbdfc15 commit fd802a3

File tree

3 files changed

+63
-43
lines changed

3 files changed

+63
-43
lines changed

Diff for: index.js

+35-19
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,32 @@ var Pool = module.exports = function (options, Client) {
2222

2323
util.inherits(Pool, EventEmitter)
2424

25+
Pool.prototype._promise = function (cb, executor) {
26+
if (!cb) {
27+
return new this.Promise(executor)
28+
}
29+
30+
function resolved (value) {
31+
process.nextTick(function () {
32+
cb(null, value)
33+
})
34+
}
35+
36+
function rejected (error) {
37+
process.nextTick(function () {
38+
cb(error)
39+
})
40+
}
41+
42+
executor(resolved, rejected)
43+
}
44+
45+
Pool.prototype._promiseNoCallback = function (callback, executor) {
46+
return callback
47+
? executor()
48+
: new this.Promise(executor)
49+
}
50+
2551
Pool.prototype._destroy = function (client) {
2652
if (client._destroying) return
2753
client._destroying = true
@@ -43,7 +69,6 @@ Pool.prototype._create = function (cb) {
4369
if (err) {
4470
this.log('client connection error:', err)
4571
cb(err)
46-
this.emit('error', err)
4772
} else {
4873
this.log('client connected')
4974
this.emit('connect', client)
@@ -53,15 +78,17 @@ Pool.prototype._create = function (cb) {
5378
}
5479

5580
Pool.prototype.connect = function (cb) {
56-
return new this.Promise(function (resolve, reject) {
81+
return this._promiseNoCallback(cb, function (resolve, reject) {
5782
this.log('acquire client begin')
5883
this.pool.acquire(function (err, client) {
5984
if (err) {
6085
this.log('acquire client. error:', err)
6186
if (cb) {
6287
cb(err, null, function () {})
88+
} else {
89+
reject(err)
6390
}
64-
return reject(err)
91+
return
6592
}
6693

6794
this.log('acquire client')
@@ -80,9 +107,9 @@ Pool.prototype.connect = function (cb) {
80107

81108
if (cb) {
82109
cb(null, client, client.release)
110+
} else {
111+
resolve(client)
83112
}
84-
85-
return resolve(client)
86113
}.bind(this))
87114
}.bind(this))
88115
}
@@ -95,36 +122,25 @@ Pool.prototype.query = function (text, values, cb) {
95122
values = undefined
96123
}
97124

98-
return new this.Promise(function (resolve, reject) {
125+
return this._promise(cb, function (resolve, reject) {
99126
this.connect(function (err, client, done) {
100127
if (err) {
101-
if (cb) {
102-
cb(err)
103-
}
104128
return reject(err)
105129
}
106130
client.query(text, values, function (err, res) {
107131
done(err)
108132
err ? reject(err) : resolve(res)
109-
if (cb) {
110-
cb(err, res)
111-
}
112133
})
113134
})
114135
}.bind(this))
115136
}
116137

117138
Pool.prototype.end = function (cb) {
118139
this.log('draining pool')
119-
return new this.Promise(function (resolve, reject) {
140+
return this._promise(cb, function (resolve, reject) {
120141
this.pool.drain(function () {
121142
this.log('pool drained, calling destroy all now')
122-
this.pool.destroyAllNow(function () {
123-
if (cb) {
124-
cb()
125-
}
126-
resolve()
127-
})
143+
this.pool.destroyAllNow(resolve)
128144
}.bind(this))
129145
}.bind(this))
130146
}

Diff for: test/events.js

+2-24
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ describe('events', function () {
2222
})
2323
})
2424

25-
it('emits "error" with a failed connection', function (done) {
25+
it('emits "connect" only with a successful connection', function (done) {
2626
var pool = new Pool({
2727
// This client will always fail to connect
2828
Client: mockClient({
@@ -34,29 +34,7 @@ describe('events', function () {
3434
pool.on('connect', function () {
3535
throw new Error('should never get here')
3636
})
37-
pool.on('error', function (err) {
38-
if (err) done()
39-
else done(new Error('expected failure'))
40-
})
41-
pool.connect()
42-
})
43-
44-
it('callback err with a failed connection', function (done) {
45-
var pool = new Pool({
46-
// This client will always fail to connect
47-
Client: mockClient({
48-
connect: function (cb) {
49-
process.nextTick(function () { cb(new Error('bad news')) })
50-
}
51-
})
52-
})
53-
pool.on('connect', function () {
54-
throw new Error('should never get here')
55-
})
56-
pool.on('error', function (err) {
57-
if (!err) done(new Error('expected failure'))
58-
})
59-
pool.connect(function (err) {
37+
pool._create(function (err) {
6038
if (err) done()
6139
else done(new Error('expected failure'))
6240
})

Diff for: test/index.js

+26
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,32 @@ describe('pool', function () {
103103
pool.end(done)
104104
})
105105
})
106+
107+
it('does not create promises when connecting', function (done) {
108+
var pool = new Pool()
109+
var returnValue = pool.connect(function (err, client, release) {
110+
release()
111+
if (err) return done(err)
112+
pool.end(done)
113+
})
114+
expect(returnValue).to.be(undefined)
115+
})
116+
117+
it('does not create promises when querying', function (done) {
118+
var pool = new Pool()
119+
var returnValue = pool.query('SELECT 1 as num', function (err) {
120+
pool.end(function () {
121+
done(err)
122+
})
123+
})
124+
expect(returnValue).to.be(undefined)
125+
})
126+
127+
it('does not create promises when ending', function (done) {
128+
var pool = new Pool()
129+
var returnValue = pool.end(done)
130+
expect(returnValue).to.be(undefined)
131+
})
106132
})
107133

108134
describe('with promises', function () {

0 commit comments

Comments
 (0)