📜 ⬆️ ⬇️

Using async / await in nodejs today

After all, have everyone been tired of callbacks in asynchronous calls and spaghetti code? Fortunately, new esync / await sugar appeared in es6 to use any asynchronous functions without a headache.

Imagine a typical task. The client put the goods in the basket, clicked "Pay". The cart must fly to the node, a new order is created, the link to the payment is returned to the customer in the payment system.

Algorithm like this:

  1. we receive from the client the product id (s) by the POST method
  2. look at the price of these goods in the database
  3. we put the order to our base
  4. send a POST request to the payment system so that it returns a unique payment link to us
  5. if everything is ok, we give this link to the client
  6. Before you write evil comments, you need to imagine that we live in a perfect world and we don’t have errors from clients, in the database, in the queries ..., so in the whole code below I will leave them to the fate, because it is not interesting

Normal code will look something like this:
')
const http = require('http'); const request = require('request'); const mysql = require('mysql') .createConnection() .connect() http.createServer( function(req, res){ //      res,         let clientRes = res; //   // ...    POST  //    id  : let order = { ids: [10,15,17], phone: '79631234567' } //    mysql.query('select cost from myshop where id in', order.ids, function(err, res){ //  res       .    ( ,      .          ) let totalPrice = 0; for(let i = 0; i < prices.result.length; i++){ totalPrice += prices.result[i].price; } //       (  ,     ) mysq.query('insert into orders set client=?, ids=?, status=1', [order.phone, order.ids.join(',')], function(err, res){ // mysql  insertId,          let insertId = res.insertId; request.post('http://api.payment.example.com', {form: { name: `  ${insertId}`, amount: totalPrice }}, function(err, res, body){ //  JSON    let link = JSON.parse(body).link; //    clientRes.end(link); //      ,       mysql.query('update orders set status=2 where id=?', insertId, function(err, res){ console.log(`  ${insertId} `); }); }); }); }); }).listen(8080); 

How many callbacks counted?

For the current day, many libraries under the node do not know how to give promises, and it is necessary to use different promisifikatory. But using promises doesn't look as cool as async / await either.

And how do you like this code?

 const http = require('http'); const request = require('request'); const promise = require('promise'); const mysql = require('mysql') .createConnection() .connect() http.createServer( async function(req, res){ //      res,         let clientRes = res; //   // ...    POST  //    id  : let order = { ids: [10,15,17], phone: '79631234567' } //   let selectCost = await promise(mysql, mysql.query, 'select cost from myshop where id in', order.ids); // ,     selectCost{ err: ..., res: ... } //   let totalPrice = 0; for(let i = 0; i < prices.result.length; i++){ totalPrice += prices.result[i].price; } //       let newOrder = await promise(mysql, mysql.query, 'insert into orders set client=?, ids=?, status=1', [order.phone, order.ids.join(',')]); let insertId = newOrder.res.insertId; //     //     let payment = await promise(request, request.post, {form: { name: `  ${insertId}`, amount: totalPrice }}); //         let link = JSON.parse(payment.res.body).link; //    clientRes.end(link); //    let updateOrder = await promise(mysql, mysql.query, 'update orders set status=2 where id=?', insertId); console.log(`  ${insertId} `); }).listen(8080); 

Not a single karbek, Karl! It looks just like in php. What happens under the hood in the promise function? The magic is pretty simple. Basically, all functions are passed to callback 2 objects: error and result. This will be the versatility of using the function:

 "use strict" function promise(context, func, ...params){ //    ,   (,  )         //     return new Promise( resolve => { // ,     func.call(context, ...params, (...callbackParams) => { //    ,     resolve,       (.  - promiseToAssoc); let returnObject = promiseToAssoc([...callbackParams]); resolve( returnedObject ); }) }) } /*         */ function promiseToAssoc(results){ let res = {}; //  3 ,         err, res  body let assoc = ['err', 'res', 'body']; for(let i = 0; i < results.length; i++){ //   (  )   field_3, field_4  . let field = assoc[i] || `field_${i}`; res[ field ] = results[i]; } return res; } module.exports = promise; 

That is, in fact, wrapping some method in this function, we will get the output

 let result = await promise(fs, fs.readFile, 'index.html'); // result = {err: null, res: ' '} 

So it goes. We put the last piece of code in a file called promise, then in the place where we need to get rid of the callback, we write const promise = require ('./ promise');

There are more elegant methods of use. You can add the names of the parameters you want to get there.

For example, when checking a file for existence (fx.exist), the method returns only one value, true or false. And with the current code, you would have to use if (fileExist.err) console.log ('file found'), which is not good. Well, it would be nice to hang a mistake on the reject.

It is also possible to perform parallel queries in a loop, such as

 var files = ['1.csv', '2.csv', '3.csv']; var results = []; //   for(let i = 0;i<files.length;i++){ results.push( promise(fs, fs.readFile, files[i]) ); } //     (   3   ,      .      ,     ) for(let i = 0;i<files.length;i++){ results[i] = await results[i]; } 

The code is used in production with a maximum recorded load of ~ 300 requests per second to our API (no, this is not an online store from the example) for about six months (just then, node 8 of the version with async / await support was released). There were no bugs, leaks or loss of performance compared to the same callbacks or promises in their pure form. Like a clock. And each method generates from 0 to 10 such awaits.

The article does not claim the Nobel Prize, and it is even scary to put such code in github. The head is ready for the stones “crutches / govnokod / bicycle”, but nevertheless, I hope someone such an idea of ​​use may be interesting.

Write in the comments, how do you use async / await?

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


All Articles