DEV Community

loading...

Using JavaScript Promises for Non-Async Control Flow?

Donald Merand
Director of Tech-sploration at EXPLO (www.explo.org)
・1 min read

I love functional languages like Elixir + Elm that give you a pipe syntax for control flow:

data
|> first_transformation
|> second_transformation
|> etc
Enter fullscreen mode Exit fullscreen mode

It's easy to understand what this program is doing! I've been learning about how promises work in JavaScript, and they seem like a good way to mimic this kind of data-chaining, even if your functions aren't really doing anything that needs to be asynchronous:

first_transformation(data)
.then(second_transformation)
.then(third_transformation)
.catch(handle_errors_from_the_whole_chain)
Enter fullscreen mode Exit fullscreen mode

The MDN docs on Control flow and error handling suggest that promises are mostly for async/deferred operations. Is it normal to use promises merely as a mechanism for control flow and organization? Is it a horrible idea for some reason?

Discussion (7)

Collapse
nickytonline profile image
Nick Taylor (he/him) • Edited

As they mention, it is for async / deferred operations so not a good use case for synchronous operations. Good news though, the pipeline operator was proposed and is moving along quite well. If you want to read about it, @kayis has an article that talks about it.

In the meantime while we wait for the pipeline operator, you could compose your functions. If you're new to composition, @kyleshevlin offers a short but very clear and informative explanation in his egghead.io video, Build Complex Functions with Function Composition in JavaScript.

Collapse
dmerand profile image
Donald Merand Author

That's good news that JS is getting a pipeline operator! I'm occasional enough with my JS coding that I prefer to skip using build environments like Babel if I can, but it'll be awesome when that feature gets wider browser support.

I haven't watched the video, but in DuckDuckGo'ing JS composition of course it makes sense - you just manually curry your functions. I've been spoiled by languages that do that for me :)

I still like the part where promises let me pass errors through + deal with them at the end... I wonder about a good composable way to do that...

Collapse
nickytonline profile image
Nick Taylor (he/him) • Edited

I haven't done much RxJS, but that's probably a good use case for piping. See RxJS 5.5, piping all the things.

Collapse
manigandham profile image
Mani Gandham

This is reasonable. The separate functions get queued as microtasks in the event loop and will probably complete in the same loop depending on how fast they return. The overhead of a Promise is minimal and modern javascript engines (if you're running this in a browser) are already well optimized for it.

The downside is you may need to continue using Promises if you want to deal with the result of the entire chain inline with other stuff, or you can just use an await at the beginning.

Collapse
dmerand profile image
Donald Merand Author

This is a good point - once you start promising you can't stop! Thanks for pointing that out - it's definitely helpful to keep in mind.

I haven't done speed tests, but I've run tests to see if I can get promises in a chain to run out of order, and I haven't found a way to break it yet. Admittedly, I'm no JavaScript hacker, but I'm generally pleased with the approach thus far.

Collapse
ttatsf profile image
tatsuo fukuchi • Edited

I love to use these functions for the function composition.

const pipe = x => (...f) => f.reduce( (acc, e) => e(acc), x )
const dot = (...f) => x => f.reduceRight( (acc, e) => e(acc), x )

//usage:
pipe(data)(first_tf, second_tf, third_tf, fourth_tf);
dot(fourth_tf, third_tf, second_tf, first_tf)(data);

The name"dot" comes from Haskell just like "pipe" from elm. It's the reverse of the pipe function.

Collapse
dmerand profile image
Donald Merand Author

Ooh those are handy. Thanks for sharing!