📜 ⬆️ ⬇️

No need to make promises or vice versa

Every programmer who starts developing under Node.js faces the choice of a strategy for organizing asynchronous code in a project. While in small system utilities, it is quite simple to maintain the hygiene of an asynchronous code, with the increase in the mass of the code in a project, the solution of this problem begins to require the introduction of an additional, so-called control flow .

This article will discuss the small control flow library “Flowy” , which is the development of Step Step Caswell's ideas of the project, and the core of which is based on CommonJS Promises concepts, and also gives arguments why Promises is so inconvenient.



')

What it looks like


function leaveMessage(username, text, callback) { Flowy( function() { // concurrent execution of two queries model.users.findOne(username, this.slot()); model.settings.findOne(username, this.slot()); }, function(err, user, settings) { // error propagating if (!user) throw new Error('user not found'); if (!settings.canReceiveMessages) throw new Error('violating privacy settings'); model.messages.create(user, text, this.slot()); }, function(err, message) { model.notifications.create(message, this.slot()); }, callback //any error will be automatically propagated to this point ); } 



Why does it look that way?


A beginner acquaintance with the API of the non-blocking I / O Node.js system is offered the following asynchronous call interface:

 fs.readFile('/etc/passwd', 'utf8', function (err, data) { if (err) throw err; console.log(data); }); 

When using someone else’s modules, it’s natural to want to have an interface similar to that described above — the rule of least surprise is one of the keys to supporting and easily debugging code. From here comes the first requirement for the library:

We want to keep the “native” nodejs-like interfaces of functions and callbacks. Each step of Flowy has a nodejs callback interface, which makes it easy to wrap the entire chain of steps into a traditional nodejs function.

In this case, the main idea of ​​Promises (as an example of the implementation will be used in the future library "Q" by Chris Cowell) is to replace the callback transfer with the last argument to the asynchronous call by creating a chain of calls to the Promise methods:

 // chaining promises: Q.fcall(step1).then(step2).then(step3).done() return getUsername() .then(function (username) { return getUser(username) .then(function (user) { // if we get here without an error, the value returned here // or the exception thrown here resolves the promise returned by the first line }) }) 

The first thing that catches your eye: functions return Promise . Thus, to use the library, it is necessary to wrap all the “classic” functions in the Promise adapter (this process is described in more detail on the project's page), or else to develop code with interfaces that are strictly library-oriented (however, all public interfaces of the module will need to be returned classic view, given the requirement formulated above). It is not comfortable. It sounds scary and no less scary. At the same time, the second requirement for the control flow library immediately comes to mind:

The library should be only a “glue” between the existing parts of the system and not become a heavy dependency. All the features of “Flowy” functioning are hidden inside the steps - that same glue - which allows functions using it to remain “clean” for the outside world. Soar should remain in the hut.

When working with libraries that allow you to create chains (chaining) of asynchronous calls, it is often necessary to perform part of the calls in parallel . The Q library provides the following awkward solution:

 Q.allResolved(promises) .then(function (promises) { promises.forEach(function (promise) { if (promise.isFulfilled()) { var value = promise.valueOf(); } else { var exception = promise.valueOf().exception; } }) }) 

In addition to everything, if we suddenly want to break the rule “one argument - one return value”, then we will have to do additional exercises:

 return getUsername() .then(function (username) { return [username, getUser(username)]; }) .spread(function (username, user) { }) 

Reading this code, one more requirement to the library suggests itself:

We want to easily execute several parallel queries and pass any number of arguments to callbacks. “Flowy” is able without any additional effort on the part of the developer due to its architecture.

So, “Flowy” is a lightweight library for managing the asynchronous flow of program execution, which makes it easy to solve everyday issues of developers under Node.js and has proven itself in the production environment.

This article demonstrates only the basic features of “Flowy”. For more information, I invite everyone to visit the project page on the githaba, where you will find abundant documentation with many examples.

Useful sources:

Source: https://habr.com/ru/post/166419/


All Articles