Skip to content

Instantly share code, notes, and snippets.

@briancavalier
Created March 15, 2011 13:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save briancavalier/870729 to your computer and use it in GitHub Desktop.
Save briancavalier/870729 to your computer and use it in GitHub Desktop.
A better, imho, when() function that normalizes its return value to a promise
// An alternative to dojo.when that always returns a promise
// The problem with dojo.when, imho, is that you have no idea
// what it will return, so if you want to chain anything, you
// end up with lots of nested when()s, like this:
// dojo.when(dojo.when(dojo.when(thing)))
//
// This alternative always returns a promise, so you can write:
// when(thing).then().then().then()
// which is much more intuitive, imho.
//
// Note that Promise is probably dojo._base.Deferred if you're
// using dojo.
//
// promised-io has a similar function named whenPromise(),
// so if you use this you may want to adopt its naming convention.
// See: https://github.com/kriszyp/promised-io/blob/master/lib/promise.js
function when(valueOrPromise) {
var promise;
if(isPromise(valueOrPromise)) {
promise = valueOrPromise;
} else {
promise = new Promise();
promise.resolve(valueOrPromise);
}
return promise;
}
// An example implementation of isPromise()
function isPromise(valueOrPromise) {
return valueOrPromise && typeof valueOrPromise.then == 'function';
}
@cjohansen
Copy link

This is a great utility, and I've used a similar one many times. I usually call it "thenable", cause it like it's quirky sound :)

@unscriptable
Copy link

/me wonders does it make sense to have an isPromise(obj) function or is sniffing for the "then" method good enough

@cjohansen
Copy link

An isPromise(obj) would at least make for a more resilient implementation.

@ded
Copy link

ded commented Mar 15, 2011

Dan's got something like this tucked away in Loadrunner as well. https://github.com/danwrong/loadrunner - really great way of storing references to defer the loading of resources

@tobie
Copy link

tobie commented Mar 15, 2011

You should defer resolving the promise through a setTimeout (https://gist.github.com/575166) to avoid unpredictable outcomes (https://gist.github.com/575161). fwiw, CJS defines promises through duck-typing.

@briancavalier
Copy link
Author

@unscriptable @cjohansen. Yep, I definitely agree about isPromise() instead of directly testing for the presence of valueOrPromise.then. I was lazy. Thanks for calling me on it :) I'll un-lazify and update it with isPromise()!

@unscriptable
Copy link

Hey @tobie, I def agree on the predictability. I wish there were a standard way to yield the current execution "thread" and continue immediately afterwards. setTimeout adds ~10 msec in most browsers and ~4 msec in Chrome (IIRC). That could be a pretty big performance penalty in some cases. It's hard for me to justify adding it to a general-purpose routine unless it were optional (although I have definitely done it, anyways).

@tobie
Copy link

tobie commented Mar 15, 2011

Agreed. The overhead is a pain. We need a process.nextTick in the browser.

@briancavalier
Copy link
Author

@tobie That is an interesting example, for sure. However, I think the "unpredictable" outcome seems more due to invalid assumptions, rather than any inherent problem with promises or my implementation of when() (or dojo's implementation, in fact). I think it is making two invalid assumptions: 1) that the two promises will resolve in the same order every time, and 2) the first promise will always resolve before the value of template is changed. Neither of those, imho is valid for an async programming model.

So, I think deferring with setTimeout may not be necessary. I'd advocate refactoring code like that to remove the assumptions. What do you think? As always, it's entirely possible that I've missed something subtle (or not so subtle!) about your example, so please let me know if I did!

Re: duck-typed promises. Yes, you are absolutely right that CommonJS says they must be feature-testable. In fact, Kris Zyp's CommonJS Promises/A proposal says specifically: "A promise is defined as an object that has a function as the value for the property 'then'" (here: http://wiki.commonjs.org/wiki/Promises/A). Wrapping that test up in a function, in some sense, just saves some typing, and will probably minify better in most compressors. However, if the method for determining whether something is a promise should ever change, it would also help with maintenance, as @unscriptable and @cjohansen hinted at.

The problem I kept running into was having to feature test the return value of dojo.when() which was tedious, or having to nest dojo.when(), which, to my eyes, is harder to read than when().then().then(). That's personal preference, of course.

@tobie
Copy link

tobie commented Mar 15, 2011

Better explanation that I could give.

Contrary to popular belief, sync programming is not a subset of async programming. The two should be considered mutually exclusive.

For example, you would have no way to collect the first chunk of data from the following stream if it somehow was cached and emitted synchronously.:

var stream = new Stream();
stream.on('data', doStuff);

@cjohansen
Copy link

"Contrary to popular belief, sync programming is not a subset of async programming. The two should be considered mutually exclusive."

Well said @tobie! :)

As for duck-typed promises, I'm aware of Kris Zyp's proposal and it's wording. I still think isPromise will make the code both easier to understand, and possibly easier to maintain in the long run.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment