Skip to content

Commit

Permalink
Prioritize .then on thenable functios (#735)
Browse files Browse the repository at this point in the history
  • Loading branch information
brianmhunt committed Jun 10, 2016
1 parent 487da8d commit 6f6c49f
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 6 deletions.
21 changes: 15 additions & 6 deletions lib/dust.js
Original file line number Diff line number Diff line change
Expand Up @@ -248,16 +248,25 @@
};

/**
* Decide somewhat-naively if something is a Thenable.
* Decide somewhat-naively if something is a Thenable. Matches Promises A+ Spec, section 1.2 “thenable” is an object or function that defines a then method."
* @param elem {*} object to inspect
* @return {Boolean} is `elem` a Thenable?
*/
dust.isThenable = function(elem) {
return elem &&
typeof elem === 'object' &&
return elem && /* Beware: `typeof null` is `object` */
(typeof elem === 'object' || typeof elem === 'function') &&
typeof elem.then === 'function';
};

/**
* Decide if an element is a function but not Thenable; it is prefereable to resolve a thenable function by its `.then` method.
* @param elem {*} target of inspection
* @return {Boolean} is `elem` a function without a `.then` property?
*/
dust.isNonThenableFunction = function(elem) {
return typeof elem === 'function' && !dust.isThenable(elem);
};

/**
* Decide very naively if something is a Stream.
* @param elem {*} object to inspect
Expand Down Expand Up @@ -430,7 +439,7 @@
}
}

if (typeof ctx === 'function') {
if (dust.isNonThenableFunction(ctx)) {
fn = function() {
try {
return ctx.apply(ctxThis, arguments);
Expand Down Expand Up @@ -747,7 +756,7 @@
};

Chunk.prototype.reference = function(elem, context, auto, filters) {
if (typeof elem === 'function') {
if (dust.isNonThenableFunction(elem)) {
elem = elem.apply(context.current(), [this, context, null, {auto: auto, filters: filters}]);
if (elem instanceof Chunk) {
return elem;
Expand All @@ -772,7 +781,7 @@
chunk = this,
i, len, head;

if (typeof elem === 'function' && !dust.isTemplateFn(elem)) {
if (dust.isNonThenableFunction(elem) && !dust.isTemplateFn(elem)) {
try {
elem = elem.apply(context.current(), [this, context, bodies, params]);
} catch(err) {
Expand Down
24 changes: 24 additions & 0 deletions test/templates/all.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ function FalsePromise(err, data) {
return defer.promise;
}

/**
* A naive function factory that adds a thenable that resolves to the given value.
* @param resolvesTo {*} The value passed to the `resolve` then argument
*/
function createThenableFunction(resolvesTo) {
var fn = function () {};
fn.then = function (res) { res(resolvesTo) };
return fn;
}

/**
* A naive Stream constructor that streams the provided array asynchronously
* @param arr {Array<Object|Error>|String} items to be streamed
Expand Down Expand Up @@ -914,6 +924,20 @@ return [
expected: "Eventually magic!",
message: "should reserve an async section for a thenable returned from a function"
},
{
name: "thenable section from thenable function",
source: "{#thenable}Eventually poof!{/thenable}",
context: { "thenable": createThenableFunction("poof!") },
expected: "Eventually poof!",
message: "should reserve an async section for a thenable function"
},
{
name: "thenable reference from thenable function",
source: "A {thenable} thing",
context: { "thenable": createThenableFunction("real") },
expected: "A real thing",
message: "should reserve an async reference for a thenable function"
},
{
name: "thenable deep section",
source: "Eventually my {#magic.ally}{delicious}{/magic.ally} will come",
Expand Down

0 comments on commit 6f6c49f

Please sign in to comment.