Javascript Promises wot I learned

I program in 2006-era Javascript. But recently I started doing some more modern stuff. Arrow functions, those are great! And easy. Promises! Those are great. And confusing. Here’s some notes about what I’ve learned about Promises.

Warning: I’m a n00b here, some of this info could be wrong.

  • The best single guide to Promises I’ve read is Google’s. The folksy tone at the start is annoying but keep reading. MDN also has a basic guide that’s useful. You’ll want the MDN reference too.
  • The key thing no one explains is that when you create a Promise, it is automatically added to the event loop and will be executed by the browser when it has time. You do not have to schedule a promise or create your own event loop, like you do in Python async programming. It’s all automagic.
  • The exciting thing about Promises compared to other async frameworks is the .then() function, the ability to chain a second promise onto a first one. “First do this thing asynchronously, then when it is completed start doing this second thing. And make this chunk of two things itself a Promise so it runs asynchronously”.
  • You can also combine Promises with all() and race() but I don’t see it being used that way very often.
  • The other neat thing about Promises is it has an API for error handling. I’ve never used this for real (I’m an optimist programmer), so I can’t say more about it, but it seems important.
  • A Promise is a Javascript function that runs like any other function, but will yield control and allow other things to run when it’s blocking on network IO. (At least I think that’s right, I’m not 100% confident.)
  • When I’m sloppy I think of a Promise as basically a Thread. An independent unit of execution that runs in parallel with everything else. That’s not really true; Promises aren’t Threads, in particular there’s no interrupting multitasking. There is co-operative multi-tasking though so as soon as your Promise blocks waiting for network or whatever, something else will happen. Specifically the browser’s event loop will keep running and the page will be responsive, the key reason to be using async programming at all.
  • It’s OK to create Promises that never invoke their resolve or reject functions. That means the Promise won’t ever have a return value to pass on to the next Promise in the then chain, but that’s OK. Maybe you wanted the side effects or something. It’s probably not a good idea though, the promise will stay in pending state forever and nothing chained after it with then() or whatever will ever run.
  • It’s also OK to mix Promises and events for different styles of asynchronous programming. Promise chains let you program in a more or less declarative way: do A then B then C. But it can be tricky to arrange your control flow for a whole page program to make sense this way. Events are nice because the publisher of the event doens’t need to know anything about who might be consuming it somewhere else. It’s fine to have a Promise post an event that something else listens to and acts on asynchronously.

This code is laughably simple, but I found it useful when learning about Promises:

 

var state = 1;

console.log("starting");

console.log(state);

p1 = new Promise((resolve, reject) => {
    console.log("p1 created");
    setTimeout(function() {
        console.log("p1 finished");
        state = 2;
        resolve();
    }, 1000);
});

console.log("made p1");
console.log(state);
p1.then(function() { console.log(state); });
console.log("made p1.then");

3 thoughts on “Javascript Promises wot I learned

  1. Nelson, this post is great! Promises are a certainly a fascinating model for async programming.

    A Promise is a Javascript function that runs like any other function, but will yield control and allow other things to run when it’s blocking on network IO. (At least I think that’s right, I’m not 100% confident.)

    This is exactly right, but Promises only superficially schedule work — they only guarantee that every Promise is executed async even if they could be resolved entirely within the current stack. This is so Promises behave consistently no matter what kinds of operations they wrap. Promises are expressible in ES5, and those implementations largely use `setTimeout(executePromise, 0)` to schedule execution on the next tick.

    The history is interesting — Promises have been around for a while as user-space abstractions to deal with the manual continuation passing “Callback Hell” http://callbackhell.com/. This was always kind of a problem but started causing a lot more pain when node.js (and the associated IO heavy workloads) started gaining traction. The Promise pattern became a popular solution, and the divergent implementations (jQuery, Q, Bluebird) were pulled into a common specification called Promises/A+: https://promisesaplus.com (this was all community organized, which is really cool) and then eventually Promises were added to the language with ES6. They’re something of a stepping stone now — the upcoming `async`/`await` keywords are basically syntactic sugar for promises.

    I’ve encountered some amazingly insidious and subtle bugs with promise usage in the wild. For example:

    const pending = {};
    function dedupedBuild(target) {
      if (pending[target]) return pending[target];
      const p = new Promise((resolve, reject) => resolve(build(target))).then(() => delete pending[target]);
      pending[target] = p;
      return p;
    }
    

    If whatever happens in `build(target)` causes the Promise to be rejected, then the `pending` cache will silently return a stale value for the life of the process :neutral_face:

    The fix is to add:

    .catch(v => delete cacheLock[target]);
    

    I’ve seen this bug or a variation on it cost days of debugging on several different projects. I’m not sure if this is part of ES6 or a V8 addition, but you’ll get runtime warnings for this now like `Uncaught (promise) error: …` which rules.

    Well, looks like I wrote a rant about Promises in here ʅʕ•ᴥ•ʔʃ. I did a lot of research on this last year and wrote a Promises/A+ implementation: https://github.com/danielmendel/micro-promise to try and fully understand the details of the spec. I was going to write a blog post detailing it but I rabbit-holed writing a D3 visualization (this is why I don’t blog).

Comments are closed.