Add to Technorati Favorites

Promises are first-class objects for function calls

Have you ever programmed in a language without functions as first-class objects? You can’t return a function from a function, can’t pass a function as an argument, and you certainly can’t make anonymous functions on the fly. Remember how liberating, empowering, and flexible it felt when you moved to a language with functions as first-class objects?

Yesterday, after 7 years of working with deferreds/promises/futures (call them what you will), thinking carefully about them thousands of times, mailing and blogging about them, giving talks about them, and now writing a book about them, I realized there is a very simple way to look at them:

Promises are first-class objects for function calls.

In other words, a promise gives you a way to make a function call and to pass around that function call. Return it from a function, pass it as an argument, put it in a data structure, etc. Given a promise, you can arrange to process the result of the function call or its error.

To be a bit more abstract: A promise is a time-independent reification of a function call.

By “time-independent” I mean that if you get a promise from somewhere, you don’t have to worry whether the underlying function call has already completed, is currently in progress, or has yet to run. Depending on the promise implementation there may be no way to find out. The point is that you don’t have to care and you shouldn’t care.

That’s all I’ll say for now, except for a final comment on naming.

I think “promise” is a better name than “future” or “deferred” because the latter two feel more like they’re referring to an event yet to happen. A promise feels more neutral: you could easily be making a promise about something you’ve already done. Many explanations of promises/deferreds/futures stress that they are something that will hold the result of a computation that’s not yet completed. That’s certainly a common usage, but it’s only part of the picture. Describing promises as a reification of a function call takes the time factor completely out of the picture.

Here’s a small Javascript function to memoize a (single-argument) function to illustrate the point:

var memoize = function(fn) {
    var promises = {};

    return function(arg) {
        var promise = promises[arg];
        if (!promise) {
            promise = promises[arg] = $.when(fn(arg)).promise();
        }
        return promise;
    };
};

The memoization cache is full of promises, not underlying function results. Some of the function calls may have completed, some may be underway. I’m using the jQuery $.when function as a convenience (that’s an irrelevant detail, don’t let it distract you). The promises stay in the cache indefinitely (no eviction, for simplicity), holding promises for function calls from the past.

Time is not an issue here.

Specifically, and in contrast, think about what happens with non-promise memoization. What happens if a first call comes in with an argument X, but before fn(X) has finished computing there is another call with argument X? Then another and another and another? You wind up calling fn(X) many times. I.e., doing exactly the thing you were trying to avoid! And there’s nothing you can do about it.

If you’re interested to review the book I’m writing with Nicholas Tollervey on jQuery deferreds, please email me or leave a comment below. Most of the book is not really specific to jQuery’s flavor of deferreds.


You can follow any responses to this entry through the RSS 2.0 feed. Both comments and pings are currently closed.

17 Responses to “Promises are first-class objects for function calls”

  1. Brilliant!
    It makes perfect sense but you are going to have to work harder to convince the beginner. Or perhaps this could be a good way to explain to the beginner what a promise is (?) Or perhaps there is a slightly more abstract version of the Promise just waiting to be invented….
    Would you like to write it up for I Programmer?
    http://www.i-programmer.info

  2. Hi Mike. Thanks for the comment. I agree re beginners. I’d be happy to write this up further. First though, I need to keep pushing on the jQuery deferred book. Maybe a write up just after that’s out would work? That would likely be December or early January. I’m terry@jon.es if you’d like to email. Thanks again!

  3. Nice. I like it (especially the idea of a “promise” which can have already been fulfilled). I’d be interested in the draft of your book. I was having lunch with @ntoll the other other day and he claimed you’d done most of the work :)

  4. Hi Tim. Great, thanks a lot! I’ll send you a PDF.

  5. Jonathan Dobson Says:

    “Promises are first-class objects for function calls.”

    Thanks for this. It allows me to slot the idea in alongside the other concepts nestled in my brain. Will keep an eye out for the book – will definitely review, if you like.

  6. Are you not simply continuing to delegate all the heavy lifting to $.when() with this pattern?
    It feels like a kind of memcached wrapper around the jQuery function…
    What in my view is really interesting is what happens when you call the value of the promise before it’s available?
    In a multi-threaded environment the call would would just block until the result was available (or some timeout was triggered)
    But in a single-threaded environment that’s a none starter, which is why we have callbacks
    So, the the idea that a call to memoize() would return either a value (if the result is cached) or a callback (if it isn’t) seems to just push the problem further up the stack.

  7. Hi David

    I probably should have explained the use of $.when. It’s in there so the memoized function always returns a promise, regardless of whether the underlying function does or not. There’s nothing special about the use of $.when, I could have just made own promise.

    The point of the example is to show that during the time between when the function is first called with an argument X and the time that that underlying call finishes, you already have a stored (promise) result that can be used in case another call with X as an argument is made. The point of the whole post is to point out that a promise makes a first class object out of a function call (i.e., a function that has been called, and which may or may not have returned already, we don’t know or care), so the code is really just an example, and is not central.

    When you say “what happens when you call the value of the promise before it’s available” it sounds like you are familiar with a different kind of future/promise, e.g., in Java, where you can ask for the value. You can’t do that with jQuery deferreds (or with Promises/A+ or with Twisted deferreds). Have you read https://en.wikipedia.org/wiki/Futures_and_promises#Blocking_vs_non-blocking_semantics ? It might throw some light on the various flavors of promises. (I am far from an expert, BTW.) Anyway, in jQuery deferreds all you can do, and it can always be done, is to register callbacks to be called when the promise value becomes available (or errors). So, as you say, there are callbacks.

    This “the idea that a call to memoize() would return either a value (if the result is cached) or a callback” isn’t accurate (maybe a typo?). I’m *always* returning a promise (definitely not a callback – whatever that is :-)). I could have made the code return either a value or a promise (making the interface less consistent, which would force the caller to check the return value type). Using $.when was convenient – if you pass it a single non-promise argument, it returns that value via an already-fired promise. I hope that make sense….

  8. Sorry Terry, you’re right – even if cached, memoized() does indeed return a reference to the relevant $.when() function.
    I misread your code, my bad.
    Nevertheless, I fail to get too excited about the whole promise/deferred/future idea, especially when multiple calls are involved.
    My favorite function in Java is InvokeAll() which allows you to request multiple futures – and it blocks until all are either populated/timed out.
    We have found that that it’s frequently cleaner to call a purpose-written invokeAll() on our server than make multiple client-side requests on the same endpoints our invokeAll() would have handled. I think people call this problem callback soup.
    I would be be very interested in a client-side library that is able to wrap multiple related promises into a single unified promise.
    Any ideas?

  9. Hi David

    I think you *should* get excited about promises, especially when multiple calls are involved. BTW, it sounds like you already are, at least in the Java world.

    The $.when function does pretty much what you describe. You can pass it multiple values (some or all of which can be promises) and it gives you back a promise that will fire when all the passed promises have fired. There’s a bit more to it than that, but mainly just details. I wrote a version with added functionality, see http://blogs.fluidinfo.com/terry/2013/07/27/jquery-when2-a-more-flexible-way-to-work-with-jquery-deferreds/

    Much of what differentiates promise implementations are the helper functions (like $.when) that can be used to combine multiple promises in some way. $.when is about the most basic / common. There are tons of examples in the Javascript world (there are 34 implementations of Promises/A+ last I counted). See e.g. https://github.com/kriskowal/q

  10. Thanks for the explanation and the links Terry.

  11. Hi again, Terry.
    I finally found some time to to read through the links (and then on to your when2 repo on github).
    The code looks very clean, I’ll pass it on to Francesco, our front-end guy.
    Look forward to reading your book when it comes out.

  12. If you’re going to start using promises in a serious way client side, there are many people, and I guess I would also be one, who would advise you not to use jQuery’s flavor. Other libraries come with utility functions that can convert a jQuery promise into a Q or when.js promise. The difference is in error handling. jQuery’s deferreds/promises are based on Promises/A, which wasn’t specific enough. Promises/A+ then came out, which really nails down the behavior a promise should provide, but jQuery has not been updated to adhere to A+. Hopefully it will be. I can tell you more about it offline if you like. I’ll send you a book PDF too :-)

  13. Hi Jonathan. Thanks for the kind offer and sorry for my very slow reply. If you send me an email I get you a draft – we’ve been working to get it finished and it’s in a pretty readable state now (we hope!). I’m terry(at)jon.es

  14. Hi Tim. Did I mail you? I can’t see any record of it. Anyway, if you’re still interested to help with reviewing the jQuery Deferreds book, you could mail me at terry(at)jon.es and I’ll reply with a PDF. Thanks!

  15. Jonathan Dobson Says:

    Done.

  16. Hi Tim. I don’t know if Nicholas has told you, but the book is out :-) See http://blogs.fluidinfo.com/terry/2014/01/07/learning-jquery-deferreds-published/ for some of the back story. Thanks again for your help!

  17. Hi Mike. If you’re still interested in a write-up for I Programmer, I could do it now. The book is out. See http://blogs.fluidinfo.com/terry/2014/01/07/learning-jquery-deferreds-published/ for some details (and a 40-50% discount code).