Comments 32
Во-первых, у нативных промисов всё ещё есть некоторые проблемы, связанные со скоростью работы и занимаемой памятью.
Во-вторых, пока нативной поддержки API на промисах нет — без promisifyAll весьма тяжело.
Кстати, а в чем разница между Apache 2.4 и nginx с точки зрения event loop, не расскажете?
Только это не повод использовать устаревший модуль. К тому же, разве Promises не позволяют достаточно изящно обрабатывать асинхронные ошибки?
Да промисы позволяют решить проблему. Но, точно знаю, что есть кучу кода который написан в old-style, не известно будут ли там внедрены промисы или нет, да и возможно ли такое внедрение не сломав API — вопрос. По этому domain все же имеет право на жизнь.
nodejs.org/api/events.html#events_events
When the EventEmitter object emits an event, all of the Functions attached to that specific event are called synchronously.
То есть какой-то неправильный способ борьбы с callback hell предлагается, разве нет?
Ничего не мешает сделать обработчики событий асинхронными, но в коде примеров в статье этого ведь не сделано.
Сказано что с помощью событий можно реализовать асинхронный код, а в примерах вместо этого написан синхронный (тут наверное уместно было бы подколоть автора что дескать сам не знает node.js — а все туда же :) )
Статья явно не первой свежести. Конструкторы Buffer получили статус deprecated. EventEmitter вовсе не способ борьбы с callback hell, это место прочно заняли Promise. А вместо console.error пора начинать использовать process.emitWarning.
Кто-то явно не знает Node.js, остается вопрос – кто?
А к деталям можно придраться в любом материале. ИМХО
Можно во всем искать только хорошее. Материал более чем на половину устарел или не соответствует действительности, а заголовок прямо указывает на обратное (отдает желтизной). Это может ввести новичков в заблуждение. Я бы на вашем месте сделал ремарку во вступительном абзаце, до того, как кто-то начнет применять эти советы на практике.
2) Если бы я был новичком и только-только открыл вашу статью, то уж точно ничего не понял бы. Статья вроде бы подразумевает, что читатели должны быть знакомы с Node, в то же время всё начинается весьма поверхностно, но не до конца разжевано. Например:
> Сравните Nginx и Apache. Именно благодаря циклу событий Node работает очень быстро
Читатель может быть не знаком с тем, что Nginx изначально асинхронный, а апач использует отдельные процессы. И далее сразу идет «именно поэтому Node работает очень быстро». Т.е. человек должен как-то сравнить два веб-сервера и прийти к выводу, что Node быстр. Что-то из разряда: «Сравните двс в Ладе и BMW. Именно поэтому самолеты такие быстрые!»
> Результат выполнения этого кода совершенно предсказуем даже для новичков в Node/JavaScript: data1->Hello Ruby->data2->Hello NODE!
Может я придираюсь, но прочитав ваш код — мне совершенно не очевидно, что там лежало в файлах, которые код должен был выдать. Там могло лежать «data1», а могла быть и «Война и мир»
И опять-же в следующем примере про асинхронное чтение файла — не факт, что первый файл прочитается быстрее, чем второй. Это зависит от FS и от размера файлов. Я думаю, это очень важно и про это стоит добавить, т.к. в этом и смысл callback'ов, что они могут вести себя весьма непредсказуемо — либо первый либо второй, зависит от разных причин. И это надо держать в голове.
Вроде примеры весьма простые, кажется что для новичков, но очень многое приходится додумывать, и будь на месте действительно новичок — ему будет многое непонятно. А для более-менее опытных — здесь мало нового. Я считаю, что если уж начали разжевывать, так разжуйте.
Как для читателя, соблазненным Node, но так и не освоившим на полную — подчеркну, что здесь приведен весьма интересный для меня материал. Однако столкнувшись с вашей статьёй на несколько лет раньше — не понял бы сути.
Еще вопрос:
Раскройте пожалуйста, что произойдет, если на один эмитер два раза повесить одно и то же событие 'knock':
emitter.on('knock', function(){})
Эмитер с вызовом функции срабоет дважды или единожды?
— Эмитер с вызовом функции срабоет дважды или единожды?
1-н раз сработает, вроде, он не chain'ит callback'и (2-а раза повесить не пробовал — вывод логический).
emitter.on('knock', aFun);
emitter.on('knock', bFun);
то при наступлении события knock будет вызваны, естественно, и слушатель aFun, и слушатель bFun (в порядке установки этих слушателей). Или вы о другом спрашивали?
А вот настоящее счастье-радость наступает с использованием ES6-генераторов скрещённых с Promise: co.js
console.log('Hello');
co(function* () {
let data1 = yield asyncCall1(); // wait for async call
console.log(data1);
let data2 = yield asyncCall2(); // wait for async call
console.log(data2);
});
console.log('World!');
Выведет следующее:
Hello
World!
[data1]
[data2]
Причём, data1 и data2 будут строго в этом порядке.
Причём остался ли он нужен сегодня, когда Babel переводит вам код с async/await в код на генераторах — тоже вопрос.
Я не говорю, что «co» плохой или что его не надо использовать, но я сомневаюсь, что это то, с чего надо сейчас начинать.
Есть ненулевой шанс, что его задепрекейтят в ближайший год.
Хочется поделиться ссылкой на проект который может оказаться весьма полезным для желающих написать свой С++ Addon для Node.js (впрочем как и для NW.js и Electron): cmake-js — данный проект позволяет использовать CMake в качестве системы сборки вместо node-gyp. Проблема в том что для большинства сторонних библиотек .gyp файлы придется писать самостоятельно (а зачем еще может понадобиться написание C/C++ addon кроме как для подключения сторонней библиотеки?), в то время как с CMake шансы избавить себя от дополнительной работы весьма высоки.
— (а зачем еще может понадобиться написание C/C++ addon кроме как для подключения сторонней библиотеки?)
Для оптимизации некоторых build-in функций — бывало такое ;)
В браузерах появился TypedArray (ES2015). В последних версиях Node «Buffer» является классом над стандартным Uint8Array API.
По пунктам:
> К слову, в Node.js всё же можно написать блокирующий код.
Вы это подаёте так: «в Node.js по умолчанию всё асинхронное, но можно использовать *Sync методы и будет как вы привыкли», не упоминая про то, что использование синхронных методов на сервере в процессе работы — выстрел себе в ноги из гранатомёта. В первую очередь в статье для новичков стоило бы на этом акцентировать внимание, а то пойдут же *Sync вызовы втыкать.
> Чтобы справиться с адом callback’ов, или пирамидой погибели (pyramid of doom), применяются эмиттеры событий.
Как уже сказали — Promises, async/await. Не надо городить ад на эмиттерах =).
> Буферы
Очень важный момент (не упомянутый в этой статье) — Buffer(number) фактически выделяет кусок памяти, в которым могут быть чувствительные данные. Именно поэтому в новой версии его разделили на Buffer.from(val) и Buffer.alloc(number)/Buffer.allocUnsafe(number) — чтобы случайно не вызвать Buffer(number) при передаче числа там, где предполагался вызов Buffer(val). Buffer.alloc(number), кстати, теперь зануляет выделяемую память (allocUnsafe — нет, но его стоит использовать очень осторожно).
> Где хранить пароли?
> Для этого существует глобальный объект,
Нет, глобальный объект существует не для этого. Вынесите ваш конфиг в модуль/файл.
> global.__filename, global.__dirname, global.module, global.require
Они все — undefined, в вашей статье ошибка.
Эти переменные определяются не на глобальном объекте, а как аргументы функции-обёртки require.
> pm2
В прошлый раз, когда я про это упоминал, местной аудитории это очень не понравилось (вплоть до ограничения на комментирование), но я всё равно повторю: не стоит думать, что pm2/forever в вакууме — надёжный способ перезапуска приложения в продакшне. Чтобы гарантировать перезапуск, надо построить цепочку ватчеров вплоть до системного инита (и до аппаратного watchdog), и никак иначе. pm2/forever тоже могут упасть (и делают это). А если вы используете pm2/forever _исключительно_ для перезапуска — тогда он вам вообще не нужен, скорее всего.
> uncaughtException
Ещё стоит упомянуть unhandledRejection.
> Обработка асинхронных ошибок
> Прослушиваем uncaughtException.
Нет, единственное, что стоит делать при возникновении uncaughtException — закрывать всё и завершать процесс, вы же сами это упоминали.
А для красивой обработки асинхронных ошибок есть Promises, можно в сочетании с async/await.
> Аддоны на C++
Первое, на что стоит смотреть для написания нативных аддонов — nan. Если вы будете дёргать v8 руками — пострадает совместимость с версиями Node.js, и при обновлении вам с большей вероятностью придётся что-то переписывать. nan берёт это на себя.
Не пропала ли здесь частица НЕ?
Вот после этого в коде примера ошибка:
Синтаксис fork() почти аналогичен синтаксису метода spawn(), за одним исключением: здесь не нужна команда, потому что fork() предполагает, что все процессы относятся к Node.js:
var fs = require('fs')
var process = require('child_process')
var p = process.fork('program.js')
p.stdout.on('data', function(data)) {
console.log('stdout: ' + data)
})
У потомка stdout и stderr равны null, и код выдаст ошибку:
TypeError: Cannot read property 'on' of null
Вы не знаете Node: краткий обзор основных возможностей