Promises

Claus Reinke claus.reinke at talk21.com
Sat Nov 10 07:05:40 PST 2012


> On further thought, I'm not so sure.  Consider this code, which creates a
> directory if it doesn't already exist and then logs "done".
> 
>    return AFS.stat(path).then(stat => {
>        if (!stat.isDirectory())
>            throw new Error("Path is not a directory.");
>    }, error => {
>        // Path doesn't exist - create the directory
>        return AFS.mkdir(path);
>    }).then(val => console.log("done"));
> 
> Breaking the error handler out into its own "fail" method seems to make
> this awkward to express because the "fail" callback is assigned to the
> "then" transform, instead of the original promise as intended.

Conditional branching is as important for method chains as it is
for statement lists. But we do not use conditionals as the basic
statement form:

    if (true) { console.log( 1 ) }
    if (true) { console.log( 2 ) }

One could also argue that promise failure is an exceptional condition,
so two-pronged 'then' encourages API designs that use exceptions for
normal branching - usually not a good sign.

> I think arrow functions make the two-arg form considerably more
> aesthetically pleasing.

Yes, but is that the only criterion? I'm not sure I can give a better API
on the spot, but there are some things that make me uneasy about
that sample. In particular, a recoverable condition (path doesn't exist)
and an unrecoverable error (path exists, is not a directory) are mixed
into the same channel (promise failure), which leads to the difficulty
you note (disentangling the conditions).

Ok, I had to try anyway;-) It took me a few attempts before I had an 
alternative that I was willing to show. How about this:

return AFS.stat(path)
    .fail(error => null) // map failure to success condition
    .then(stat =>

        if (!stat)    // Path doesn't exist - create the directory
            return AFS.mkdir(path);
        else if (stat.isDirectory())    // done already
            return 'nothing to do';
        else    // no can do
            throw new Error("Path is not a directory.");

    ).then(val => console.log("done"));

We can then provide a standard promise method for the mapping 
from success/failure to nullable conditions, such as

    'cond' : cb => this.fail(error => null).then(cb)

and the code would become as pleasing as and the intentions 
arguably clearer than in the original example.

Claus
 


More information about the es-discuss mailing list