Skip to content

Commit da60b8b

Browse files
committed
feat: use native Promise instead of Bluebird, and allow users to switch back.
BREAKING CHANGE: This change makes all the code that rely on the features provided by Bluebird not working anymore. For example, `redis.get('foo').timeout(500)` now should be failed since the native Promise doesn't support the `timeout` method. You can switch back to the Bluebird implementation by setting `Redis.Promise`: ``` const Redis = require('ioredis') Redis.Promise = require('bluebird') const redis = new Redis() // Use bluebird assert.equal(redis.get().constructor, require('bluebird')) // You can change the Promise implementation at any time: Redis.Promise = global.Promise assert.equal(redis.get().constructor, global.Promise) ```
1 parent 229f264 commit da60b8b

20 files changed

+185
-102
lines changed

README.md

+19-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ used in the world's biggest online commerce company [Alibaba](http://www.alibaba
1616

1717
0. Full-featured. It supports [Cluster](http://redis.io/topics/cluster-tutorial), [Sentinel](http://redis.io/topics/sentinel), [Pipelining](http://redis.io/topics/pipelining) and of course [Lua scripting](http://redis.io/commands/eval) & [Pub/Sub](http://redis.io/topics/pubsub) (with the support of binary messages).
1818
0. High performance.
19-
0. Delightful API. It works with Node callbacks and [Bluebird promises](https://github.com/petkaantonov/bluebird).
19+
0. Delightful API. It works with Node callbacks and Native promises.
2020
0. Transformation of command arguments and replies.
2121
0. Transparent key prefixing.
2222
0. Abstraction for Lua scripting, allowing you to define custom commands.
@@ -944,6 +944,24 @@ var redis = new Redis();
944944
redis.set('foo');
945945
```
946946

947+
# Plugging in your own Promises Library
948+
If you're an advanced user, you may want to plug in your own promise library like [bluebird](https://www.npmjs.com/package/bluebird). Just set Redis.Promise to your favorite ES6-style promise constructor and ioredis will use it.
949+
950+
```javascript
951+
const Redis = require('ioredis')
952+
Redis.Promise = require('bluebird')
953+
954+
const redis = new Redis()
955+
956+
// Use bluebird
957+
assert.equal(redis.get().constructor, require('bluebird'))
958+
959+
// You can change the Promise implementation at any time:
960+
Redis.Promise = global.Promise
961+
assert.equal(redis.get().constructor, global.Promise)
962+
```
963+
964+
947965
# Running tests
948966

949967
Start a Redis server on 127.0.0.1:6379, and then:

index.js

+10
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@ exports.Promise = require('bluebird');
77
exports.Cluster = require('./lib/cluster');
88
exports.Command = require('./lib/command');
99

10+
var PromiseContainer = require('./lib/promise_container');
11+
Object.defineProperty(exports, 'Promise', {
12+
get: function() {
13+
return PromiseContainer.get();
14+
},
15+
set: function(lib) {
16+
PromiseContainer.set(lib);
17+
}
18+
});
19+
1020
exports.print = function (err, reply) {
1121
if (err) {
1222
console.log('Error: ' + err);

lib/cluster/connection_pool.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ ConnectionPool.prototype.findOrCreate = function (node, readOnly) {
3636
readOnly = Boolean(readOnly);
3737

3838
if (this.specifiedOptions[node.key]) {
39-
_.assign(node, this.specifiedOptions[node.key]);
39+
Object.assign(node, this.specifiedOptions[node.key]);
4040
} else {
4141
this.specifiedOptions[node.key] = node;
4242
}

lib/cluster/index.js

+15-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
'use strict';
22

3-
var Promise = require('bluebird');
43
var Deque = require('denque');
54
var Redis = require('../redis');
65
var utils = require('../utils');
@@ -12,8 +11,10 @@ var ScanStream = require('../scan_stream');
1211
var Commander = require('../commander');
1312
var Command = require('../command');
1413
var commands = require('redis-commands');
14+
var asCallback = require('standard-as-callback');
1515
var ConnectionPool = require('./connection_pool');
1616
var DelayQueue = require('./delay_queue');
17+
var PromiseContainer = require('../promise_container');
1718

1819
/**
1920
* Creates a Redis Cluster instance
@@ -112,7 +113,7 @@ Cluster.defaultOptions = {
112113
};
113114

114115
util.inherits(Cluster, EventEmitter);
115-
_.assign(Cluster.prototype, Commander.prototype);
116+
Object.assign(Cluster.prototype, Commander.prototype);
116117

117118
Cluster.prototype.resetOfflineQueue = function () {
118119
this.offlineQueue = new Deque();
@@ -141,6 +142,7 @@ Cluster.prototype.connect = function () {
141142
this.resetNodesRefreshInterval();
142143
}
143144

145+
var Promise = PromiseContainer.get();
144146
return new Promise(function (resolve, reject) {
145147
if (this.status === 'connecting' || this.status === 'connect' || this.status === 'ready') {
146148
reject(new Error('Redis is already connecting/connected'));
@@ -271,8 +273,10 @@ Cluster.prototype.quit = function (callback) {
271273
clearInterval(this.slotsTimer);
272274
this.slotsTimer = null;
273275
}
276+
277+
var Promise = PromiseContainer.get();
274278
if (status === 'wait') {
275-
var ret = Promise.resolve('OK').nodeify(callback);
279+
var ret = asCallback(Promise.resolve('OK'), callback)
276280

277281
// use setImmediate to make sure "close" event
278282
// being emitted after quit() is returned
@@ -283,11 +287,14 @@ Cluster.prototype.quit = function (callback) {
283287

284288
return ret;
285289
}
286-
return Promise.all(this.nodes().map(function (node) {
287-
return node.quit();
288-
})).then(function () {
289-
return 'OK';
290-
}).nodeify(callback);
290+
return asCallback(
291+
Promise.all(this.nodes().map(function (node) {
292+
return node.quit();
293+
})).then(function () {
294+
return 'OK';
295+
}),
296+
callback
297+
);
291298
};
292299

293300
/**

lib/command.js

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
'use strict';
22

33
var _ = require('./utils/lodash');
4-
var Promise = require('bluebird');
54
var fbuffer = require('flexbuffer');
65
var utils = require('./utils');
76
var commands = require('redis-commands');
87
var calculateSlot = require('cluster-key-slot');
8+
var PromiseContainer = require('./promise_container');
9+
var asCallback = require('standard-as-callback');
910

1011
/**
1112
* Command instance
@@ -60,7 +61,8 @@ function Command(name, args, options, callback) {
6061

6162
Command.prototype.initPromise = function () {
6263
var _this = this;
63-
this.promise = new Promise(function (resolve, reject) {
64+
var Promise = PromiseContainer.get();
65+
var promise = new Promise(function (resolve, reject) {
6466
if (!_this.transformed) {
6567
_this.transformed = true;
6668
var transformer = Command._transformer.argument[_this.name];
@@ -78,7 +80,9 @@ Command.prototype.initPromise = function () {
7880
} else {
7981
_this.reject = reject;
8082
}
81-
}).nodeify(this.callback);
83+
});
84+
85+
this.promise = asCallback(promise, this.callback);
8286
};
8387

8488
Command.prototype.getSlot = function () {

lib/commander.js

+10-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
var _ = require('./utils/lodash');
44
var Command = require('./command');
55
var Script = require('./script');
6-
var Promise = require('bluebird');
6+
var PromiseContainer = require('./promise_container');
7+
var asCallback = require('standard-as-callback');
78

89
var DROP_BUFFER_SUPPORT_ERROR = '*Buffer methods are not available ' +
910
'because "dropBufferSupport" option is enabled.' +
@@ -114,7 +115,10 @@ function generateFunction(_commandName, _encoding) {
114115
var options;
115116
if (this.options.dropBufferSupport) {
116117
if (!_encoding) {
117-
return Promise.reject(new Error(DROP_BUFFER_SUPPORT_ERROR)).nodeify(callback);
118+
return asCallback(
119+
PromiseContainer.get().reject(new Error(DROP_BUFFER_SUPPORT_ERROR)),
120+
callback
121+
)
118122
}
119123
options = { replyEncoding: null };
120124
} else {
@@ -150,7 +154,10 @@ function generateScriptingFunction(_script, _encoding) {
150154
var options;
151155
if (this.options.dropBufferSupport) {
152156
if (!_encoding) {
153-
return Promise.reject(new Error(DROP_BUFFER_SUPPORT_ERROR)).nodeify(callback);
157+
return asCallback(
158+
PromiseContainer.get().reject(new Error(DROP_BUFFER_SUPPORT_ERROR)),
159+
callback
160+
)
154161
}
155162
options = { replyEncoding: null };
156163
} else {

lib/connectors/connector.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ Connector.prototype.connect = function (callback) {
2929
connectionOptions = _.pick(this.options, ['port', 'host', 'family']);
3030
}
3131
if (this.options.tls) {
32-
_.assign(connectionOptions, this.options.tls);
32+
Object.assign(connectionOptions, this.options.tls);
3333
}
3434

3535
var _this = this;

lib/pipeline.js

+7-4
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ var _ = require('./utils/lodash');
33
var Commander = require('./commander');
44
var Command = require('./command');
55
var fbuffer = require('flexbuffer');
6-
var Promise = require('bluebird');
76
var util = require('util');
7+
var asCallback = require('standard-as-callback');
88
var commands = require('redis-commands');
99
var calculateSlot = require('cluster-key-slot');
10+
var PromiseContainer = require('./promise_container');
1011

1112
function Pipeline(redis) {
1213
Commander.call(this);
@@ -20,13 +21,14 @@ function Pipeline(redis) {
2021
this._shaToScript = {};
2122

2223
var _this = this;
23-
_.keys(redis.scriptsSet).forEach(function (name) {
24+
Object.keys(redis.scriptsSet).forEach(function (name) {
2425
var script = redis.scriptsSet[name];
2526
_this._shaToScript[script.sha] = script;
2627
_this[name] = redis[name];
2728
_this[name + 'Buffer'] = redis[name + 'Buffer'];
2829
});
2930

31+
var Promise = PromiseContainer.get()
3032
this.promise = new Promise(function (resolve, reject) {
3133
_this.resolve = resolve;
3234
_this.reject = reject;
@@ -39,7 +41,7 @@ function Pipeline(redis) {
3941
});
4042
}
4143

42-
_.assign(Pipeline.prototype, Commander.prototype);
44+
Object.assign(Pipeline.prototype, Commander.prototype);
4345

4446
Pipeline.prototype.fillResult = function (value, position) {
4547
var i;
@@ -213,7 +215,7 @@ Pipeline.prototype.exec = function (callback) {
213215
}
214216
if (!this.nodeifiedPromise) {
215217
this.nodeifiedPromise = true;
216-
this.promise.nodeify(callback);
218+
asCallback(this.promise, callback);
217219
}
218220
if (_.isEmpty(this._queue)) {
219221
this.resolve([]);
@@ -273,6 +275,7 @@ Pipeline.prototype.exec = function (callback) {
273275
pending.push(scripts[i]);
274276
}
275277
}
278+
var Promise = PromiseContainer.get()
276279
return Promise.all(pending.map(function (script) {
277280
return _this.redis.script('load', script.lua);
278281
}));

lib/promise_container.js

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
exports.isPromise = function (obj) {
2+
return !!obj &&
3+
(typeof obj === 'object' || typeof obj === 'function') &&
4+
typeof obj.then === 'function'
5+
}
6+
7+
let promise = global.Promise
8+
9+
exports.get = function () {
10+
return promise
11+
}
12+
13+
exports.set = function (lib) {
14+
if (typeof lib !== 'function') {
15+
throw new Error(`Provided Promise must be a function, got ${lib}`)
16+
}
17+
18+
promise = lib
19+
}

lib/redis.js

+19-11
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,18 @@
33
var _ = require('./utils/lodash');
44
var util = require('util');
55
var EventEmitter = require('events').EventEmitter;
6-
var Promise = require('bluebird');
76
var Deque = require('denque');
87
var Command = require('./command');
98
var Commander = require('./commander');
109
var utils = require('./utils');
10+
var asCallback = require('standard-as-callback');
1111
var eventHandler = require('./redis/event_handler');
1212
var debug = require('./utils/debug')('ioredis:redis');
1313
var Connector = require('./connectors/connector');
1414
var SentinelConnector = require('./connectors/sentinel_connector');
1515
var ScanStream = require('./scan_stream');
1616
var commands = require('redis-commands');
17+
var PromiseContainer = require('./promise_container');
1718

1819
/**
1920
* Creates a Redis instance
@@ -131,7 +132,7 @@ function Redis() {
131132
}
132133

133134
util.inherits(Redis, EventEmitter);
134-
_.assign(Redis.prototype, Commander.prototype);
135+
Object.assign(Redis.prototype, Commander.prototype);
135136

136137
/**
137138
* Create a Redis instance
@@ -248,7 +249,8 @@ Redis.prototype.setStatus = function (status, arg) {
248249
* @public
249250
*/
250251
Redis.prototype.connect = function (callback) {
251-
return new Promise(function (resolve, reject) {
252+
var Promise = PromiseContainer.get();
253+
var promise = new Promise(function (resolve, reject) {
252254
if (this.status === 'connecting' || this.status === 'connect' || this.status === 'ready') {
253255
reject(new Error('Redis is already connecting/connected'));
254256
return;
@@ -315,7 +317,9 @@ Redis.prototype.connect = function (callback) {
315317
}, function (type, err) {
316318
_this.silentEmit(type, err);
317319
});
318-
}.bind(this)).nodeify(callback);
320+
}.bind(this))
321+
322+
return asCallback(promise, callback)
319323
};
320324

321325
/**
@@ -362,7 +366,7 @@ Redis.prototype.end = function () {
362366
* @public
363367
*/
364368
Redis.prototype.duplicate = function (override) {
365-
return new Redis(_.assign(_.cloneDeep(this.options), override || {}));
369+
return new Redis(Object.assign(_.cloneDeep(this.options), override || {}));
366370
};
367371

368372
/**
@@ -511,11 +515,15 @@ Redis.prototype.monitor = function (callback) {
511515
lazyConnect: false
512516
});
513517

514-
return new Promise(function (resolve) {
515-
monitorInstance.once('monitoring', function () {
516-
resolve(monitorInstance);
517-
});
518-
}).nodeify(callback);
518+
var Promise = PromiseContainer.get();
519+
return asCallback(
520+
new Promise(function (resolve) {
521+
monitorInstance.once('monitoring', function () {
522+
resolve(monitorInstance);
523+
});
524+
}),
525+
callback
526+
);
519527
};
520528

521529
require('./transaction').addTransactionSupport(Redis.prototype);
@@ -636,6 +644,6 @@ Redis.prototype.sendCommand = function (command, stream) {
636644
};
637645
});
638646

639-
_.assign(Redis.prototype, require('./redis/parser'));
647+
Object.assign(Redis.prototype, require('./redis/parser'));
640648

641649
module.exports = Redis;

0 commit comments

Comments
 (0)