ES2017 async/await в Node.js

В этой статье разберемся что не так с промисами, как ключевое слово await поможет решить эту проблему, и как использовать это прямо сейчас.

Одним из самых лучших нововведений в ES7 считается синтаксис async/await. Хотя это всего лишь синтаксический сахар поверх Promises, эти два ключевых слова помогут писать асинхронный код намного приятней, а также помогут решить проблему callback hell, и позволят работать с асинхронным кодом так как с синхронным.

Проблема промисов

Концепция «promise» в JavaScript, довольно давно используется, с помощью таких библиотек как q, Bluebird. Только в стандарте ES6 была добавлена нативная поддержка promises.

Промисы в большинстве случаев решили проблему callback hell, но, например, когда нужно сделать несколько поочередных запросов, код становится трудночитаем.
Скажем нам нужно получить данные из стороннего RESTful API. Для выполнения HTTP запросов будем использовать библиотеку request-promise.

const request = require('request-promise');
let options = {
    url: 'https://mydomain.com/api/users',
    headers: {
        'token':'some-jwt-token'
    }
};
request.get(options).then(function(body) {
    var response = JSON.parse(body);
    console.log('response',response);
})

Сделать один запрос не так уж сложно с использованием Promises, но что если нужно получить информацию из других запросов? Или если нужно добавить управляющее операторы (if/else, циклы). С усложнением требований к функции код с промисами становится сложнее. Все же лучше, чем код с использованием колбеков. Для более сложных сценариев как в примере ниже, нужен механизм формирование цепочки промисов и понимание, когда и где ваш асинхронный код выполняется.

const request = require('request-promise');

let options = {
    url: 'https://mydomain.com/api/users',
    headers: {
        'token':'some-jwt-token'
    }
};
request.get(options).then(function(body) {
    var users = JSON.parse(body);
    users.filter(user>=user.balance>1000);
    resolve( users);
}).then(function(users) {

   let promise=[];

   users.forEach(user=>{
   let options = {
          url: 'https://mydomain.com/api/transactions/'+user.id,
          headers: {
              'token':'some-jwt-token'
          }
      };
      promises.push(request.get(options));
   })
   return Promise.all(promises)

}).then(function(userTransactions) {
    console.log('userTransactions',userTransactions)
});

Упрощение кода с помощью Async/Await

Новый синтаксис async/await позволяет использовать старый механизм Promises, но устраняет необходимость цепочки Promises. Значение, которое передается в функцию then (), будет возвращено так, как будто это был вызов синхронной функции.

const {promisify} = require('util');
const fs = require('fs');
const readFile = promisify(fs.readFile);

async function testReadFile(path){
    let fileContents=await readFile(path);
    console.log('fileContents:',fileContents)
}
testReadFile();

Хотя изменения кажется небольшим, но оно полностью меняет структуру асинхронного JavaScript кода. Нужно только добавить ключевое слово async перед именем функции, после этого можно использовать await для всех функций которые возвращают Promise.

const request = require('request-promise');

async function  download(url,name){
  let img=await request.get(url);
  img=resizeImg(img);
  let storageKey=await S3.writeFile(name,img);
  await db.imgages.create({storageKey,name})
}

download('https://async-img-download.com/await-img.jpg')

Использование Async/Await в Node.js

Возможность использовать механизм async/await была добавлена с версии 7.6.0.
В более ранних версиях при зупуске нужно добавлять аргумент командной строки —harmony-async-await.