Как вы пишете на сервере (node.js)
Много информации по написанию на node.js. Но чем больше ее изучаешь, тем понимаешь, что строгого подхода к написанию кода в node.js нет.
В этой теме хотелось бы понять при каких обстоятельствах использовать функции обратного вызова и события в node.js Когда-то можно и не прерывать процесс. Запросили данные, подождали, получили. Когда-то нужно с вызовом передать функцию обратного вызова (callback). И на другом конце обернуть ее process.nextTick() Наверное кто-то весь код "склеивает" событиями eventEmitter. Поделитесь опытом в каких случаях какие подходы задействуете. Спасибо! |
Главные правила ноды (из личного опыта, примерно ~ 2 года работы с нодой):
1) Не бросай исключений в асинхронных операциях, а передавай их как параметр callback; 2) Используй паттерны / фреймворки для работы с асинхронностью. Выбор фреймворка и паттерна зависит от предпочтений, я использую Async. 3) Нельзя по долгу занимать поток в серверном приложении, т.е. если нужно что-то большое посчитать, то либо используй setImmediate и дроби задачу, либо выноси задачу из Ноды на более предпочтительный инструмент (имхо, более правильное решение). Всё остальное уже не так важно, т.е. пиши как нравиться. |
Цитата:
|
Цитата:
Но на самом деле не важно на чём писать. Я обычно юзаю простую связку через REST, т.к. эту схему элементарно масштабировать и легко понять. |
kobezzza, а можешь дать пример кода, если не тяжело .
|
У тебя есть:
1) Кластер серверов на ноде, который принимает и агрегирует входящие запросы от клиентов; 2) Кластер серверов на Java/C++/Erlang и т.д., которые делают какие то жёсткие вычисления в 100500 потоков и т.д. Нужно организовать общение двух этих кластеров: берём банальный REST. В качестве транспорта можно взять тот же HTTP, организуем понятный для обоих сторон протокол и вуаля. Собственно по ссылке на википедии дан исчерпывающий ответ: Цитата:
|
Цитата:
спасибо. |
Цитата:
кстати, что ты имеешь в виду под "node.js" ? веб-сервер? так это ж только один компонент node.js наверное ты имел в виду express\connect :) я тоже долго допирал до того, как можно организовать код, чтобы вручную не прописывать route'инги, ибо это ну очень меня выматывает. решение оказалось простым - это было Цитата:
![]() я хочу сказать - ответ "везде, где есть ввод\вывод". но это я так думаю :) Цитата:
kobezzza, а почему стали делать именно так ? Цитата:
|
Цитата:
Или если рассматривать yield подход, то использование try-catch тоже не шибко нравиться, т.к. синтаксис конструкции слишком громоздкий и цена за её использование высока. Может я что-то не понимаю в этой жизни, но на мой взгяд это супер очевидно и удобно :)
async.map(['file1', 'file2', 'file3'], fs.stat, function (err, results) {
...
});
async.parallel(
[
function () { ... },
function () { ... }
],
function (err, results) {
...
}
);
Цитата:
А вот использование генераторов в других задачах я принял с восторгом и уже активно юзаю на сервере, собственно по этому я сейчас тружусь над новой версией Collection с поддержкой генераторов. |
Цитата:
try {
setImmediate(function () {
throw new Error('test error');
});
} catch (error) {
console.log(error); //не поймает test error
}
|
Цитата:
|
Цитата:
Я понимаю для чего эти методы, но я не знаю где их применить Например waterfall:
async.waterfall([
function(callback){
callback(null, 'one', 'two');
},
function(arg1, arg2, callback){
// arg1 now equals 'one' and arg2 now equals 'two'
callback(null, 'three');
},
function(arg1, callback){
// arg1 now equals 'three'
callback(null, 'done');
}
], function (err, result) {
// result now equals 'done'
});
обходит несколько функций, результат которых идет к следующей и так до конца. Но на деле то, как с этим работать? У меня много моментов в коде, когда метод одного модуля задействует метод другого модуля. Вот пример моего кода:
var config = require('../config');
var maxPlayers = config.get('game:maxPlayers');
// проверяет наличие свободные мест
// true: отсутствие мест
exports.check = function (users, cb) {
var result
// тут какие-то расчеты
process.nextTick(function () {
cb(result);
});
};
var waiting = require('../lib/waiting');
var allUsers = 10;
waiting.check(allUsers, function (waiting) {
if (waiting) {
} else {
}
});
Передалось, расчеты сделаны, со следующим тиком callback вернет результат. Где мне тут использовать этот async? Также если есть примеры кода (или проект) с использованием async, поделись! А то статей про async много, а в рабочем исполнении не находил. По статьям невозможно понять как это все вместе работает )) |
Цитата:
имею в виду процесс взаимодействия модулей между собой. Не обязательно express\connect Цитата:
|
Цитата:
Так или иначе, скоро в Nodejs будут доступны встроенные ES6 Promises, думаю, это повлияет на API большинства новых модулей, а может быть и самого Nodejs, я бы уже начал писать асинхронные скрипты в таком стиле:
// проверяет наличие свободные мест
exports.check = function (users) {
return new Promise(function (resolve, reject) {
setImmediate(function () {
try {
var value;
// тут какие-то расчеты
resolve(value);
} catch (reason) {
reject(reason);
}
});
});
};
var waiting = require('../lib/waiting');
var allUsers = 10;
waiting.check(allUsers).then(function (value) {
if (value) {
//есть свободные места
} else {
//нет свободных мест
}
}, function (reason) {
//произошла ошибка
});
|
Цитата:
До вызова callback происходят некоторые расчеты, далее пауза и вызов callback уже в порядке следующего тика. |
Цитата:
зацени этот говнокод. рылся по истории git'а, чтобы специально показать тебе пример: Задача: добавить категорию (родительскую\дочернюю - зависит от параметра запроса)
async.waterfall([
// тут получаем родительскую категорию, если можно
function (done) {
if (fields.id == -1) {
done(null, null);
} else {
models.ProductCategoryModel.findById(fields.id, function (err, parentCategory) { done(err, parentCategory); });
}
},
// тут определяем дочернюю категорию
function (parentCategory, done) {
var newCategory;
if (parentCategory) {
newCategory = new models.ProductSubCategoryModel();
} else {
newCategory = new models.ProductCategoryModel();
}
newCategory.title = fields.new_category_name;
if (parentCategory) {
done(null, parentCategory, newCategory);
} else {
newCategory.save(function (err, newCategory) { done(err, parentCategory, newCategory); });
}
},
// связываем родительскую и дочернюю категории
function (parentCategory, childCategory, done) {
if (parentCategory) {
parentCategory.children.push(childCategory);
parentCategory.save(done);
} else {
done(null, null);
}
}
], function (err, parentCateg) {
if (err) return next(err);
allIsCompleted(err);
});
Цитата:
|
У меня самые частые - это waterfall и parallel
Пример с waterfall:
// Получение данных пользователя
async.waterfall([
// Получаем данные пользователя по сессии
function (cb) {
},
// Получаем данные пользователя по запросу
function (sessionUser, cb) {
},
// Делаем проверку доступа и если всё ок,
// то отдаём данные
function (requestUser, sessionUser, cb) {
},
], callback);
Для задач дробления через setImmediate или nextTick прекрасно подходят циклы async:
var count = 0;
async.whilst(
function () { return count < 5; },
function (callback) {
count++;
setImmediate(function () {
...
callback();
});
},
function (err) {
...
}
);
Цитата:
Дело в том, что при работа на сервере в ноде часто работа идёт с потоками, т.е. данные принимаются / отдаются чанками, что полностью противостоит модели промисов, когда данные получаются сразу. Делается это, как я уже говорил, из соображений эффективности. Так что какой-то революции нативные промисы не сделают, но всё же хорошо, что их добавят :) |
Цитата:
раньше такого термина не встречал, а теперь он появился, вместе с появлением оператора yield используется в этой либе: https://github.com/visionmedia/co я так понял, с yield и thunk асинхронный код пишется, как синхронный. и он без callback'ов UPD. вру :) thunk - частично исполняемая функция, которая принимает один аргумент - callback. примерчики :
// read - это thunk
function read(callback) {
fs.readFile('myfile.md', 'utf8', callback);
}
// readFile - генератор thunk'ов ?
function readFile(filename) {
return function(callback) {
fs.readFile(filename, 'utf8', callback);
};
}
|
Цитата:
Цитата:
Да и отлов ошибок через try-catch смущает, т.к. поведение JIT для кода внутри таких конструкций очень осторожно, а иногда и просто не применяется. *** В общем я обязательно попробую Co в работе, чтобы на практике подтвердить или опровергнуть свои опасения :) Но пока я всецело за простые колбеки с роутерами, будь то промисы или ещё что, т.к. это просто наглядней, имхо :)
co(function *(){
var a = yield get('http://google.com');
var b = yield get('http://yahoo.com');
var c = yield get('http://cloudup.com');
console.log(a[0].statusCode);
console.log(b[0].statusCode);
console.log(c[0].statusCode);
})()
VS
async.map(['http://google.com', 'http://yahoo.com', 'http://cloudup.com'], get, (err, results) => {
...
});
Ну хз, мне второй вариант более по душе :) |
Цитата:
|
Цитата:
co и Q.async на самом деле ничего сложного не делают, а просто автоматически выстраивают такую штуку:
gen.next().value.then(function (value1) {
gen.next(value1).value.then(function (value2) {
gen.next(value2).value.then(function (value3) {
gen.next(value3)...
});
})
});
Вот рабочий пример для понимания:
function asyncGet1(value) {
return new Promise(function (resolve) {
setImmediate(resolve, value);
});
}
function asyncGet2(value) {
return new Promise(function (resolve) {
setImmediate(resolve, value + 1);
});
}
function asyncGet3(value) {
return new Promise(function (resolve) {
setImmediate(resolve, value * 2);
});
}
function * genFunc() {
var x = yield asyncGet1(1);
console.log('x = ' + x); // x = 1
var y = yield asyncGet2(x);
console.log('y = ' + y); // y = 2
var z = yield asyncGet3(y);
console.log('z = ' + z); // z = 4
}
//вместо co или Q.async:
var gen = genFunc();
gen.next().value.then(function (x) {
gen.next(x).value.then(function (y) {
gen.next(y).value.then(function (z) {
gen.next(z);
});
})
});
yield никакой асинхронностью не занимается, он просто останавливает выполнение генератора до следующего вызова next.Когда пишешь генератор, можно представлять, что co в паре с yield разбивают genFunc на вот такие «подфункции»:
function next1() {
asyncGet1(1).then(next2);
}
function next2(value) {
x = value;
asyncGet2(x).then(next3);
}
function next3(value) {
y = value;
asyncGet3(y).then(next4);
}
function next4(value) {
z = value;
}
Здесь специально расписал через value, чтобы воспринималось, как promise value.Кстати, в ES7 возможно будет реализация этого паттерна на уровне языка. Будут введены инструкции async (вместо co, Q.async) и await (вместо yield), и генераторы для это не нужны будут:
async function func() {
var x = await asyncGet1(1);
var y = await asyncGet2(x);
var z = await asyncGet3(y);
}
|
Цитата:
|
Цитата:
|
по мойму проще сервант на кокока завести, кокока куда лучше с js дружет
|
Цитата:
|
Цитата:
Цитата:
Цитата:
|
Цитата:
|
Цитата:
Цитата:
|
Цитата:
Цитата:
|
Цитата:
давненько хотел начать писать в функциональном стиле на JS в последний раз видел LiveScript, когда он только появился. он сейчас готов к продакшену? трудно ли начать писать на нём? выглядит страшновато |
c последовательной асинхронностью поступаю также как и dmitry111. Никакой пользы от async в этом моменте на практике не увидел.
когда нужно сделать несколько параллельных запроса тоже не умничаю. Просто отсчитываю количество.
function X(options, end) {
var J = 3; function complete() {
if (--J !== 0) return;
end(true);
};
// ...
process.nextTick(function () {
complete();
});
process.nextTick(function () {
complete();
});
process.nextTick(function () {
complete();
});
}
X({...}, function(status, data) {
// ...
})
|
Цитата:
Цитата:
Цитата:
|
| Часовой пояс GMT +3, время: 12:50. |