Fill the missing piece of asynchronous programming in JS/TS with concurrency.libx.js

Elya Livshitz
2 min readJun 6, 2020

In the beginning, there was hell. This hell is made of Callbacks.

Writing asynchronous code in JS was challenging from it’s beginning.
It is very easy ending up with a chaotic callback chains of functions.
If you work in organization with couple years old JS code, it’s highly likely there are legacy code that looks like this:

request(function(a, cb){
request(a, function(b, cb){
request(b, function(c, cb){
request(c, function(d, cb){
...
cb();
});
});
});
});

With the introduction of Promises in ECMAScript 2015 (or even earlier with jQuery’s Deferred or other libraries implementing Promises), our life became a bit more easier and allowed us to convert callback-based functions to Promise-based functions.
Mountains of callback blocks could be easily now chained with .then .

Though, even with the magic of Promise and async/await it’s somewhat cumbersome to handle nested functions, especially when attempting to migrate legacy code to awaitable format.

Introducing concurrency.libx.js (https://github.com/Livshitz/concurrency.libx.js)

A lightweight set of utilities for everyday JS project.
Features of concurrency.libx.js:

  • The missing piece in asynchronous programming — Leverage Promises by manually creating Deferred object (like Promise but with more control).
  • Advanced utils for concurrent events — Throttle, debounce, chain and more.
  • Easily convert synchronous functions to asynchronous — Dynamically convert functions to async functions.

Use Deferred to easily convert non-Promise, callback-based, legacy code to use Promise and be awaitble:

Consider the following block of code that expresses a callback-based legacy code:

function myWrapper(cb) {
request('something.com/list.json', function(data, err) {
console.log('list data:', data);
if (err) return cb(null, err);
if (cb) cb(data);
});
}
myWrapper((res, err)=> {
if (error) console.log('Failed :(');
console.log('Success :)', res);
});

Use Deferred to return Promise object, no matter how deep the resolution is in the legacy code:

function myWrapper() {
const d = new Deferred();
request('something.com/list.json', function(data, err) {
console.log('list data:', data);
if (err) return d.reject(err);
d.resolve(data);
});
return d;
}
try {
const result = await myWrapper();
console.log('Success :)', result);
catch (ex) {
console.log('Failed :(
}

Note that his approach does not require you to modify the function’s signature and it can remain without the async keyword.

With minimal effort you’ve converted your code into much more readable and maintainable code.

More info about utils such Throttle, Debounce, Chain and more could be found in here.

It's worth noting that concurrency.libx.js is not innovative, it’s marly wrapping a Deferred implementation provided by Facebook, and coule other helper utils. It’s rather a marely small excerpt, mandatory piece of util, that is badly missing from modern JS runtime.

concurrency.libx.js is a part of a bigger library of many handy tools which I’m gradually breaking down into independant packages: libx.js.

--

--

Elya Livshitz

Passionate technologist with over a decade experience, eager to learn new technologies, challenges-killer and out-of-the-box thinker. zuz.to/liv