📜 ⬆️ ⬇️

Another node.js library ...

I think we can again reset the time counter of the appearance of the next JS library.


It all started about 6 years ago when I met node.js. About 3 years ago, I started using node.js on projects along with the wonderful express.js library (on the wiki it is called the application framework, although some may call express the framework or even the package). Express combines the node.js http server and middleware system, created in the image of the Ruby Sinatra framework.


We all know about the speed of creating new libraries and the speed of JS development. After splitting and merging with IO.js, node.js took for itself the best from the JS world - ES6, and in April, ES7.


I want to talk about one of these changes. Specifically about async / await and Promise . Trying to use Promise in projects on express, and after async / await with the flag for node.js 7 --harmony, I came across an interesting new generation framework - koa.js, and specifically its second version .


The first version was created using generators and the CO library. The second version promises convenience when working with Promise / async / await and is waiting for the April release of node.js with support for these features without flags.


It was interesting for me to look into the koa core and learn how to work with Promise. But I was surprised, because The kernel remains almost the same as in the previous version. The authors of both express and koa libraries are the same, not surprisingly, the approach has remained the same. I mean middleware structure. Using the Ruby approach was useful at the stage of becoming node.js, but modern node.js, like JS, has its advantages, beauty, elegance ...


A bit of theory.


Node.js http ( https ) server inherits net.Server , which implements EventEmitter . And all the libraries (express, koa ...) are essentially the event handlers for the server.on ('request') event.
For example:


const http = require('http'); const server = http.createServer((request, response) => { //   }); 

Or


 const server = http.createServer(); server.on('request', (request, response) => { //     }); 

And I imagined how the “new generation framework” should look like:


 const server = http.createServer( (req, res) => { Promise.resolve({ req, res }).then(ctx => { ctx.res.writeHead(200, {'Content-Type': 'text/plain'}); ctx.res.end('OK'); return ctx; }); }); 

This gives an excellent opportunity to get rid of callback hell and constant error handling at all levels, as, for example, implemented in express. Also, this allows you to use Promise.all () for "parallel" execution of middleware instead of sequential.


And so another library appeared: YEPS - Yet Another Event Promised Server.


YEPS syntax conveys all the simplicity and elegance of a promise based design, for example, parallel processing of middleware:


 const App = require('yeps'); const app = new App(); const error = require('yeps-error'); const logger = require('yeps-logger'); app.all([ logger(), error() ]); app.then(async ctx => { ctx.res.writeHead(200, {'Content-Type': 'text/plain'}); ctx.res.end('Ok'); }); app.catch(async (err, ctx) => { ctx.res.writeHead(500); ctx.res.end(err.message); }); 

Or


 app.all([ logger(), error() ]).then(async ctx => { ctx.res.writeHead(200, {'Content-Type': 'text/plain'}); ctx.res.end('Ok'); }).catch(async (err, ctx) => { ctx.res.writeHead(500); ctx.res.end(err.message); }); 

For example, there are packages error , logger , redis .


But the most surprising was the speed of work. You can run a benchmark benchmark test — yeps-benchmark , where you compare the performance of YEPS with express , koa2, and even node.js http .


As you can see, parallel execution shows interesting results. Although this can be achieved in any project, this approach should be built into the architecture, the idea itself is not to take a single step without performance testing. For example, the library core, yeps-promisify , uses array.slice (0), the fastest array copy method .


The possibility of parallel execution of middleware prompted the idea of ​​creating a router (router), completely created on Promise.all (). The idea to catch (catch) the desired route (route), the desired rule and, accordingly, return the desired handler is the basis of Promise.all ().


 const Router = require('yeps-router'); const router = new Router(); router.catch({ method: 'GET', url: '/' }).then(async ctx => { ctx.res.writeHead(200); ctx.res.end('homepage'); }); router.get('/test').then(async ctx => { ctx.res.writeHead(200); ctx.res.end('test'); }).post('/test/:id').then(async ctx => { ctx.res.writeHead(200); ctx.res.end(ctx.request.params.id); }); app.then(router.resolve()); 

Instead of sequentially iterating through all the rules, you can simultaneously run a check of all. This moment did not remain without performance testing and the results were not long in coming.


The search for the first rule was about 10% faster. The last rule worked exactly at the same speed, which is about 4 times faster than other libraries (here we are talking about 10 routes). You no longer need to collect and analyze statistics, to think what rule to raise up ,.


But for a complete production ready work, it was necessary to solve the " chicken and egg " problem - no one would use the library without additional packages and no one would write packages to the unused library. The wrapper helped here, which allows the use of middleware from express, for example body-parser or serve-favicon ...


 const error = require('yeps-error'); const wrapper = require('yeps-express-wrapper'); const bodyParser = require('body-parser'); const favicon = require('serve-favicon'); const path = require('path'); app.then( wrapper(favicon(path.join(__dirname, 'public', 'favicon.ico'))) ).all([ error(), wrapper(bodyParser.json()), ]); 

There is also an application template - yeps-boilerplate , which allows you to launch a new application, view the code, examples ...


I hope this study and the result will be useful, maybe even inspire you to create beautiful, fast, maybe even elegant solutions. And of course, the idea of ​​testing the performance of each step should form the basis of any new and existing project.


PS: I hope for advice, ideas and constructive criticism in the comments.


')

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


All Articles