diff --git a/README.md b/README.md index 06e789c76b..a03106eabb 100644 --- a/README.md +++ b/README.md @@ -53,11 +53,30 @@ Note that the API is entire asynchronous. To get data back from the server, you'll need to use a callback. The return value from most of the API is a backpressure indicator. +### Promises + You can also use node_redis with promises by promisifying node_redis with [bluebird](https://github.com/petkaantonov/bluebird) as in: ```js var redis = require('redis'); bluebird.promisifyAll(redis.RedisClient.prototype); +bluebird.promisifyAll(redis.Multi.prototype); +``` + +It'll add a *Async* to all node_redis functions (e.g. return client.getAsync().then()) + +```js +// We expect a value 'foo': 'bar' to be present +// So instead of writing client.get('foo', cb); you have to write: +return client.getAsync('foo').then(function(res) { + console.log(res); // => 'bar' +}); + +// Using multi with promises looks like: + +return client.multi().get('foo').execAsync().then(function(res) { + console.log(res); // => 'bar' +}); ``` ### Sending Commands diff --git a/package.json b/package.json index 208bdbbbb2..15953e49a7 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,8 @@ "optional-dev-dependency": "^1.1.0", "tcp-port-used": "^0.1.2", "uuid": "^2.0.1", - "win-spawn": "^2.0.0" + "win-spawn": "^2.0.0", + "bluebird": "^2.10.0" }, "repository": { "type": "git", diff --git a/test/commands/mget.spec.js b/test/commands/mget.spec.js index 082721e386..54f48a7cff 100644 --- a/test/commands/mget.spec.js +++ b/test/commands/mget.spec.js @@ -52,6 +52,16 @@ describe("The 'mget' method", function () { }); }); + it('handles fetching multiple keys, when some keys do not exist promisified', function () { + return client.MGETAsync("mget keys 1", ["some random shit", "mget keys 2", "mget keys 3"]).then(function (results) { + assert.strictEqual(4, results.length); + assert.strictEqual("mget val 1", results[0].toString()); + assert.strictEqual(null, results[1]); + assert.strictEqual("mget val 2", results[2].toString()); + assert.strictEqual("mget val 3", results[3].toString()); + }); + }); + afterEach(function () { client.end(); }); diff --git a/test/commands/multi.spec.js b/test/commands/multi.spec.js index bd3d10979d..5ce3fa82b0 100644 --- a/test/commands/multi.spec.js +++ b/test/commands/multi.spec.js @@ -38,6 +38,12 @@ describe("The 'multi' method", function () { done(); }); }); + + it("reports an error if promisified", function () { + return client.multi().execAsync().catch(function(err) { + assert(err.message.match(/The connection has already been closed/)); + }); + }); }); describe("when connected", function () { @@ -238,6 +244,22 @@ describe("The 'multi' method", function () { }); }); + it('allows multiple commands to work the same as normal to be performed using a chaining API promisified', function () { + return client.multi() + .mset(['some', '10', 'keys', '20']) + .incr(['some', helper.isNumber(11)]) + .incr(['keys'], helper.isNumber(21)) + .mget('some', 'keys') + .execAsync() + .then(function (replies) { + assert.equal('OK', replies[0]); + assert.equal(11, replies[1]); + assert.equal(21, replies[2]); + assert.equal(11, replies[3][0].toString()); + assert.equal(21, replies[3][1].toString()); + }); + }); + it('allows an array to be provided indicating multiple operations to perform', function (done) { // test nested multi-bulk replies with nulls. client.multi([ @@ -299,6 +321,19 @@ describe("The 'multi' method", function () { }); }); + it('reports multiple exceptions when they occur (while EXEC is running) promisified', function () { + return client.multi().config("bar").debug("foo").eval("return {err='this is an error'}", 0).execAsync().then(function (reply) { + assert.strictEqual(reply.length, 3); + assert.equal(reply[0].code, 'ERR'); + assert.equal(reply[0].command, 'CONFIG'); + assert.equal(reply[2].code, undefined); + assert.equal(reply[2].command, 'EVAL'); + assert(/^this is an error/.test(reply[2].message)); + assert(/^ERR/.test(reply[0].message), "Error message should begin with ERR"); + assert(/^ERR/.test(reply[1].message), "Error message should begin with ERR"); + }); + }); + it('reports multiple exceptions when they occur (while EXEC is running) and calls cb', function (done) { var multi = client.multi(); multi.config("bar", helper.isError()); diff --git a/test/lib/config.js b/test/lib/config.js index c6f1b25dae..ed7ef640c5 100644 --- a/test/lib/config.js +++ b/test/lib/config.js @@ -3,6 +3,11 @@ // helpers for configuring a redis client in // its various modes, ipV6, ipV4, socket. var redis = require('../../index'); +var bluebird = require('bluebird'); + +// Promisify everything +bluebird.promisifyAll(redis.RedisClient.prototype); +bluebird.promisifyAll(redis.Multi.prototype); var config = { redis: redis, diff --git a/test/lib/redis-process.js b/test/lib/redis-process.js index 6082648452..465afdbf2e 100644 --- a/test/lib/redis-process.js +++ b/test/lib/redis-process.js @@ -15,18 +15,15 @@ function waitForRedis (available, cb) { var ipV4 = false; var id = setInterval(function () { - tcpPortUsed.check(config.PORT, '127.0.0.1') - .then(function (_ipV4) { - ipV4 = _ipV4; - return tcpPortUsed.check(config.PORT, '::1'); - }) - .then(function (ipV6) { - if (ipV6 === available && ipV4 === available && - fs.existsSync('/tmp/redis.sock') === available) { - clearInterval(id); - return cb(); - } - }); + tcpPortUsed.check(config.PORT, '127.0.0.1').then(function (_ipV4) { + ipV4 = _ipV4; + return tcpPortUsed.check(config.PORT, '::1'); + }).then(function (ipV6) { + if (ipV6 === available && ipV4 === available && fs.existsSync('/tmp/redis.sock') === available) { + clearInterval(id); + return cb(); + } + }); }, 100); }