Two additions for the "Promises" chapter

418 views
Skip to first unread message

Mörre Noseshine

unread,
Feb 8, 2016, 6:56:58 AM2/8/16
to Exploring ES6
I have found that some things I encounter regularly have very little coverage on the Internet (blog posts etc.):


1) Mixing synchronous and promise code.

For example, I have to do a little bit of (synchronous) computing and then start a chain of asynchronous actions, and it's all inside one function (it makes no sense to spread - less readable - and it would not change the problem). The issue: exceptions! Synchronous code throws, promises don't but send exceptions to a "catch" handler.

So when I have a function like

function (a) {
    const b = doSyncStuff(a);

    return promiseReturningFn(b)
    .then(result => {
        cont k = doWhatEver(result);
        return doSyncOrAsyncStuff(result);
    });
    // no catch() because the promise is returned and caller needs to have a catch()
}

then the promise part won't throw, but the initial part may. This is inconsistent, who wants to have a function that can raise exceptions in two wildly different ways? So either I make sure that ALL exceptions end up in the returned promise by putting the initial sync. code inside one, or I add a catch() at the end that throws anything caught within the promise code part.


2) Promise trees

Actual code is not always so straightforward as the examples used everywhere to explain promises. They always are one of 
- single promises
- an array of promises - Promise.all() and maybe a ".map()" handle that
- chains of the above

But sometimes we end up with a more complicated promise structure, more like trees - or even more generally graphs.

A nice solution in that case is - I have seen only one blog post about this but forgot the URL - to assign individual pieces of the graph to variables and then Promise.all() on all those variables.The actual execution sequence sorts itself out automatically. Then use destructuring to make sense of the array of results that is returned - this would look very confusing without destructuring because an array implies that the values inside are all related. This may not be the case at all, because "promises" are all about HOW the work was done due to hardware constraints, not WHICH work was done.


3) Different async. code parts need to share variables, and/or a result is needed much further down the (async) chain

Each then() has its own function. Sharing variables and results between different asynchronous parts has to be done a) via variables in an outer scope or b) by handing it through the chain. "Function purists" won't like to have a variable outside the (then()) function - it's not "pure". However, that solution is much more natural. to what is meant. If we didn't have the slow-hardware problem and could write synchronous code that's how we would do it, because we would not be within an inner then() function to begin with! And handing down results through the chain has its (readability/manageability) limits.

However, when we have a variable that is shared between different then() functions we may run into another problem:


4) Promise-code may look synchronous, but even in single-threaded JS we get problems of parallelism

I hardly ever see this mentioned.

The code (which is a bad example for what I'm going to say I admit)

function delay(i) {
    return new Promise((resolve, reject) => {
setTimeout(() => {console.log(i); resolve();}, Math.round(Math.random() * 100));
});
}

delay(1)
.then(() => delay(2))
.then(() => delay(3));

delay('a')
.then(() => delay('b'))
.then(() => delay('c'));

produces a randomly interleaving sequence. Obviously, when considering that each "then()" delays execution of the rest of the function until the next round of the event loop. So anything inside a "then()" runs delayed. I can't imagine that this won't catch a lot of people off guard? In actual synchronous code it is guaranteed that a function runs uninterrupted from beginning to exit. You may say "But this is obvious, after all, this is for asynchronous code!". Well, the problem is how promises is touted as "asynchronous programming now looks like synchronous code". And it does! With events and their handlers being separate functions it was much more obvious also for less advanced JS programmers that there was a "break". Now the break happens within the same function. People will stumble over this.

My previous point 3) could easily lead to cases where this bites the programmer, when an outer (shared) variable changes in unexpected ways (when it's being written to by then() functions). This can happen when some promises' functions are run in parellel. Itt already happened to me once :-)

So statements of the kind "promises make code LOOK synchronous" should regularly be accompanied by warnings that point out the word "look" and show examples demonstrating that when we use promises we may run into the same problems that other languages supporting parallelism have always had - and that the single-thread nature of Javascript no longer fully protects us.

Axel Rauschmayer

unread,
Mar 5, 2016, 4:36:27 PM3/5/16
to Exploring ES6
This is good stuff. I’m not sure how much I’ll be able to use (I don’t want to add much more to the book), so you should write a blog post (what you have written basically already is one). Ping me once it’s done and I’ll tell people about it on Twitter.

Mörre Noseshine

unread,
Mar 8, 2016, 4:53:52 PM3/8/16
to Exploring ES6
The only problem is I don't write blog posts...

Axel Rauschmayer

unread,
Mar 8, 2016, 4:56:23 PM3/8/16
to Exploring ES6
Well, in a way, you just did. What’s keeping you from blogging?

Mörre Noseshine

unread,
Mar 8, 2016, 5:04:18 PM3/8/16
to Exploring ES6
I once started an ambitious website,invested A LOT of effort into producing really cool multimedia content. Useless, a handful of accidental and lost visitors. Unless one has a really good (business) plan and lots of backing it's just a waste of time. I rather contribute to others when I feel I have something to add (and let them sort it out if it's useful). I even just gave away my very own last-name domain. For free. I'd prefer to contribute - best anonymously - to a large good platform where content is evaluated and sorted and voted by the users rather than adding the billionth unread blog to the web.

Axel Rauschmayer

unread,
Mar 13, 2016, 1:44:19 PM3/13/16
to Exploring ES6
Got it, thanks!
Reply all
Reply to author
Forward
0 new messages