ES6 Generators и Promises в node.js на практике

Эта статья Quickstart по работе с асинхронными операциями в node.js. Эпоха callback'ов прошла, наступила эра генераторов и промисов. Генераторы делают промисы более удобными. А тот факт, что node.js начал поддерживать генераторы и ещё некоторые фичи es6 из коробки, даёт нам возможность применять их на практике в продакшене.

Рассмотрим следующий пример. Структура данных такая: есть пользователи, у пользователя есть маршруты, у маршрутов есть точки. Функция delete репозитиория TripRepository проверяет, что удаляемый маршрут принадлежит указанному пользователю, а потом удаляет все точки этого маршрута и сам маршрут. При этом все запросы к базе асинхронные, а запросы на удаление не блокируют друг-друга, потому мы их можем выполнять одновременно.

var Promise = require('bluebird');

class TripRepository extends Repository {
    ...
	delete(userId, tripId) {
		var self = this;
		return Promise.coroutine(function*(){
			var trip = yield self.findOne({_id: tripId, user: userId});
			if (!trip) {
				return;
			}
			yield Promise.all([
				self._points.deleteOne({trip: tripId}),
				self._trips.deleteOne({_id : tripId})
			]);
		})();
	};
}

Теперь рассмотрим сделаем следующую последовательность действий: поищем в базе маршрут, если его нет, то создадим, а потом удалим его предыдущей функцией TripRepository.delete

var testPromise = Promise.coroutine(function*(){
    var trip = yield repo.getById('11');
    if (!trip) {
        yield repo.add({_id: '11', user: 'musuk'});
    }
    yield repo.delete('musuk', '11');
})();

testPromise.then(() => {
    console.log('coroutine executed');
})

Думаю, что код выглядит достаточно понятно. А теперь вещи, на которые стоит обратить внимание.

  • Для работы с промисами и генераторами, я использовал библиотеку bluebird, она дополняет функциональность стандартных es6-промисов.
  • Promise.coroutine — это функция bluebird, которая создаёт генератор.
  • var self = this; я написал поскольку внтури генератора this уже не будет указывать на объект класса TripRepository.
  • Promise.coroutine(function*(){})(); возвращает промис, который можно использовать дальше.

И, в заключении, пример того, как можно сделать запрос к MySQL базе из node.js:

var Promise = require('bluebird');
var co = Promise.coroutine;

var mysql = require('mysql');

Promise.promisifyAll(mysql);
Promise.promisifyAll(require("mysql/lib/Connection").prototype);
Promise.promisifyAll(require("mysql/lib/Pool").prototype);

co(function*(){
    var connection = mysql.createConnection({
        host     : 'localhost',
        user     : 'root',
        password : 'password',
        database : 'test'
    });

    yield connection.connectAsync();

    var result = yield connection.queryAsync('select * from test.users');
    console.log(result)
    connection.end();
})();