Nodejs. Побеждая ад
версия для печатиЧерновик. В будущем планирую найти таки красивое решение и не катить бочку на nodejs =)
Применительно к ноде есть такое понятие, как "callback hell" (глубоко вложенные колбэки). На хабре есть об этом. В попытках избежать этой засады я стал разбирать код на части и попал в другой кошмарик, с ассинхронной работой. Конечно есть решения и в мануалах все просто. Но стоит написать в ноде что-то чуть сложнее "Hello, world!", как начинаются проблемы. Код с катастрофической скоростью обрастает парными скобками "})", анонимными функциями и вложенными callback-вызовами. На данном этапе работы с ним я искрене не понимаю, как можно тащиться от nodejs и компании.
Работаем с модулем async. Мануал по нему полезен чуть более, чем нисколько. Задача: нужно вызвать последовательно несколько методов так, чтобы следующий метод использовал результат предыдущего. При этом ловим ошибки разумеется. Даже в простом случае получаем такое:
var async = require('async');
var result; //итог работы серии методов
async.waterfall(
[
//Хрень первая: без обертки в анонимную функцию работать не будет.
//Хрень вторая: назначить отдельную функцию в качестве callback нельзя.
function(callback){
//попытка разбить код на части. Выносим в отдельную процедуру
externalFunc(callback);
},
function(ids, callback){
//какие-то еще действия с данными от предыдущей функции
ids += 6;
callback(null, ids);
},
],
//Ошибки полученные в серии можно обработать только тут
function(err, res) {
if (err)
//самый простой вариант
console.log(err);
else
//отдаем наружу то, что получилось
result = res;
}
);
//.. и получим 0!
console.log(result);
function externalFunc(asyncback) {
//Так превываем выполнение метода из серии
//return asyncback(new Error('ops!'));
ids = 1 + 4;
asyncback(null, ids);
}
Много лишнего кода, но это жертва в пользу синхронности. Однако это не все. Если в externalFunc() будет вызов какой-нибудь функции (наследника EventEmmitter ?), способной затупить с ответом, то выполнение серии ее ждать не будет! Т.е. там нужно как-то организовать отдельный async.series(), который должен работать в паре с родителем.. Не уверен, что такое вообще возможно.
Еще одна засада: переданный наружу результат придет позже, чем наступит его очередь. Это потому, что все, что за пределами async.*, выполняется без ожидания серии. Т.о. если нужно продолжить работу с результатом серии, то только в самом async-блоке.
Если говорить о практической ситуации: получение данных через Sphinx + MySQL. Сначала нужно подключиться к Сфинксу, по запросу получить ids, потом подключиться к MySQL и по ids получить данные. Как я не изголялся, но моя серия упорно не хотела ждать, когда придет ответ от MySQL. А если добавить еще корректную реакцию на ошибки подключения с прерыванием выполнения программы, тогда начинается реальный ад. В итоге убив несколько часов я накатал код без разбиения на отдельные блоки. Прям так, портянкой, с кучей вложенных вызовов и tab-отступами на полстраницы. Прелесть..
Только вот не надо кидаться тапками, что мол, я нихрена не шарю и в nodejs не разобрался.. Я шарю, но не в ноде :) и считаю, что он реально не удобен для работы. Конечно, у него есть и плюсы, это мощная прокачка психики кодера :)
[1oo%, EoF]Понравилась статья? Расскажите о ней друзьям: