Skip to content

Commit

Permalink
feat: support returning promises from middleware functions
Browse files Browse the repository at this point in the history
  • Loading branch information
vkarpov15 committed Dec 22, 2017
1 parent 76325fa commit 05b4480
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 83 deletions.
167 changes: 84 additions & 83 deletions index.js
Expand Up @@ -32,21 +32,7 @@ Kareem.prototype.execPre = function(name, context, args, callback) {

if (pre.isAsync) {
var args = [
decorateNextFn(function(error) {
if (error) {
if (done) {
return;
}
done = true;
return callback(error);
}

++currentPre;
if (asyncPresLeft === 0 && currentPre >= numPres) {
return callback(null);
}
next.apply(context, arguments);
}),
decorateNextFn(_next),
decorateNextFn(function(error) {
if (error) {
if (done) {
Expand All @@ -61,65 +47,64 @@ Kareem.prototype.execPre = function(name, context, args, callback) {
})
];

try {
pre.fn.apply(context, args);
} catch (error) {
return args[0](error);
}
callMiddlewareFunction(pre.fn, context, args, args[0]);
} else if (pre.fn.length > 0) {
var args = [
decorateNextFn(function(error) {
if (error) {
if (done) {
return;
}
done = true;
return callback(error);
}

if (++currentPre >= numPres) {
if (asyncPresLeft > 0) {
// Leave parallel hooks to run
return;
} else {
return callback(null);
}
}

next.apply(context, arguments);
})
];
var args = [decorateNextFn(_next)];
var _args = arguments.length >= 2 ? arguments : [null].concat($args);
for (var i = 1; i < _args.length; ++i) {
args.push(_args[i]);
}
try {
pre.fn.apply(context, args);
} catch (error) {
return args[0](error);
}

callMiddlewareFunction(pre.fn, context, args, args[0]);
} else {
var error = null;
let error = null;
let maybePromise = null;
try {
pre.fn.call(context);
maybePromise = pre.fn.call(context);
} catch (err) {
error = err;
}
if (++currentPre >= numPres) {
if (asyncPresLeft > 0) {
// Leave parallel hooks to run
return;
} else {
return process.nextTick(function() {
callback(error);
});

if (isPromise(maybePromise)) {
maybePromise.then(() => _next(), err => _next(err));
} else {
if (++currentPre >= numPres) {
if (asyncPresLeft > 0) {
// Leave parallel hooks to run
return;
} else {
return process.nextTick(function() {
callback(error);
});
}
}
next(error);
}
next(error);
}
};

next.apply(null, [null].concat(args));

function _next(error) {
if (error) {
if (done) {
return;
}
done = true;
return callback(error);
}

if (++currentPre >= numPres) {
if (asyncPresLeft > 0) {
// Leave parallel hooks to run
return;
} else {
return callback(null);
}
}

next.apply(context, arguments);
}
};

Kareem.prototype.execPreSync = function(name, context, args) {
Expand Down Expand Up @@ -174,18 +159,29 @@ Kareem.prototype.execPost = function(name, context, args, options, callback) {
}
next();
});
try {
post.apply(context, [firstError].concat(newArgs).concat([_cb]));
} catch (error) {
_cb(error);
}

callMiddlewareFunction(post, context,
[firstError].concat(newArgs).concat([_cb]), _cb);
} else {
if (++currentPost >= numPosts) {
return callback.call(null, firstError);
}
next();
}
} else {
const _cb = decorateNextFn(function(error) {
if (error) {
firstError = error;
return next();
}

if (++currentPost >= numPosts) {
return callback.apply(null, [null].concat(args));
}

next();
});

if (post.length === numArgs + 2) {
// Skip error handlers if no error
if (++currentPost >= numPosts) {
Expand All @@ -194,33 +190,21 @@ Kareem.prototype.execPost = function(name, context, args, options, callback) {
return next();
}
if (post.length === numArgs + 1) {
var _cb = decorateNextFn(function(error) {
if (error) {
firstError = error;
return next();
}

if (++currentPost >= numPosts) {
return callback.apply(null, [null].concat(args));
}

next();
});

try {
post.apply(context, newArgs.concat([_cb]));
} catch (error) {
_cb(error);
}
callMiddlewareFunction(post, context, newArgs.concat([_cb]), _cb);
} else {
var error;
let error;
let maybePromise;
try {
post.apply(context, newArgs);
maybePromise = post.apply(context, newArgs);
} catch (err) {
error = err;
firstError = err;
}

if (isPromise(maybePromise)) {
return maybePromise.then(() => _cb(), err => _cb(err));
}

if (++currentPost >= numPosts) {
return callback.apply(null, [error].concat(args));
}
Expand Down Expand Up @@ -396,6 +380,23 @@ function get(obj, key, def) {
return def;
}

function callMiddlewareFunction(fn, context, args, next) {
let maybePromise;
try {
maybePromise = fn.apply(context, args);
} catch (error) {
return next(error);
}

if (isPromise(maybePromise)) {
maybePromise.then(() => next(), err => next(err));
}
}

function isPromise(v) {
return v != null && typeof v.then === 'function';
}

function decorateNextFn(fn) {
var called = false;
var _this = this;
Expand Down
22 changes: 22 additions & 0 deletions test/examples.test.js
Expand Up @@ -143,6 +143,28 @@ describe('pre hooks', function() {
done();
});
});

/* You can also return a promise from your pre hooks instead of calling
* `next()`. When the returned promise resolves, kareem will kick off the
* next middleware.
*/
it('supports returning a promise', function(done) {
hooks.pre('cook', function() {
return new Promise(resolve => {
setTimeout(() => {
this.bacon = 3;
resolve();
}, 100);
});
});

var obj = { bacon: 0 };

hooks.execPre('cook', obj, function() {
assert.equal(3, obj.bacon);
done();
});
});
});

describe('post hooks', function() {
Expand Down
19 changes: 19 additions & 0 deletions test/post.test.js
Expand Up @@ -131,6 +131,25 @@ describe('execPost', function() {
done();
});
});

it('supports returning a promise', function(done) {
var calledPost = 0;

hooks.post('cook', function() {
return new Promise(resolve => {
setTimeout(() => {
++calledPost;
resolve();
}, 100);
});
});

hooks.execPost('cook', null, [], {}, function(error) {
assert.ifError(error);
assert.equal(calledPost, 1);
done();
});
});
});

describe('execPostSync', function() {
Expand Down

0 comments on commit 05b4480

Please sign in to comment.