Commit 628099a7 authored by Domenic Denicola's avatar Domenic Denicola

Add `RSVP.denodeify`.

Converts functions that use the Node.js-style callbacks as their last parameter into promise-returning functions. Such functions also accept promises, in order to allow cool composition (see tests for example).
parent 38f4a266
......@@ -166,4 +166,35 @@ function defer() {
return deferred;
}
export { Promise, all, defer, configure };
function makeNodeCallbackFor(promise) {
return function (error, value) {
if (error) {
reject(promise, error);
} else if (arguments.length > 2) {
resolve(promise, Array.prototype.slice.call(arguments, 1));
} else {
resolve(promise, value);
}
};
}
function denodeify(nodeFunc) {
return function() {
var nodeArgs = Array.prototype.slice.call(arguments);
var promise = new Promise();
all(nodeArgs).then(function(nodeArgs) {
nodeArgs.push(makeNodeCallbackFor(promise));
try {
nodeFunc.apply(this, nodeArgs);
} catch(e) {
reject(promise, e);
}
});
return promise;
};
}
export { Promise, all, defer, denodeify, configure };
......@@ -26,6 +26,129 @@ describe("RSVP extensions", function() {
});
});
describe("RSVP.denodeify", function() {
specify('it should exist', function() {
assert(RSVP.denodeify);
});
specify('calls node function with any arguments passed', function(done) {
var args = null;
function nodeFunc(arg1, arg2, arg3, cb) {
args = [arg1, arg2, arg3];
cb();
}
var denodeifiedFunc = RSVP.denodeify(nodeFunc);
denodeifiedFunc(1, 2, 3).then(function() {
assert.deepEqual(args, [1, 2, 3]);
done();
});
});
specify('waits for promise/thenable arguments to settle before passing them to the node function', function(done) {
var args = null;
function nodeFunc(arg1, arg2, arg3, cb) {
args = [arg1, arg2, arg3];
cb();
}
var denodeifiedFunc = RSVP.denodeify(nodeFunc);
var promise = new RSVP.Promise(function(resolve) { resolve(1); });
var thenable = { then: function (onFulfilled) { onFulfilled(2); } };
var nonPromise = 3;
denodeifiedFunc(promise, thenable, nonPromise).then(function() {
assert.deepEqual(args, [1, 2, 3]);
done();
});
});
specify('fulfilled with value if node function calls back with a single argument', function(done) {
function nodeFunc(cb) {
cb(null, 'nodeFuncResult');
}
var denodeifiedFunc = RSVP.denodeify(nodeFunc);
denodeifiedFunc().then(function(value) {
assert.equal(value, 'nodeFuncResult');
done();
});
});
specify('fulfilled with array if node function calls back with multiple arguments', function(done) {
function nodeFunc(cb) {
cb(null, 1, 2, 3);
}
var denodeifiedFunc = RSVP.denodeify(nodeFunc);
denodeifiedFunc().then(function(value) {
assert.deepEqual(value, [1, 2, 3]);
done();
});
});
specify('rejected if node function calls back with error', function(done) {
function nodeFunc(cb) {
cb('bad!');
}
var denodeifiedFunc = RSVP.denodeify(nodeFunc);
denodeifiedFunc().then(function() {
assert(false);
done();
}, function(reason) {
assert.equal(reason, 'bad!');
done();
});
});
specify('rejected if node function throws an exception synchronously', function(done) {
function nodeFunc(cb) {
throw 'bad!';
}
var denodeifiedFunc = RSVP.denodeify(nodeFunc);
denodeifiedFunc().then(function() {
assert(false);
done();
}, function(reason) {
assert.equal(reason, 'bad!');
done();
});
});
specify('integration test showing how awesome this can be', function(done) {
function readFile(fileName, cb) {
setTimeout(function() {
cb(null, 'contents of ' + fileName);
}, 0);
}
var writtenTo = null;
function writeFile(fileName, text, cb) {
setTimeout(function () {
writtenTo = [fileName, text];
cb();
}, 0);
}
var denodeifiedReadFile = RSVP.denodeify(readFile);
var denodeifiedWriteFile = RSVP.denodeify(writeFile);
denodeifiedWriteFile('dest.txt', denodeifiedReadFile('src.txt')).then(function () {
assert.deepEqual(writtenTo, ['dest.txt', 'contents of src.txt']);
done();
});
});
});
describe("RSVP.all", function() {
specify('it should exist', function() {
assert(RSVP.all);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment