Компания
963,94
рейтинг
16 апреля 2015 в 18:42

Разработка → 10 основных ошибок при разработке на Node.js перевод



C момента появления Node.js его и критикуют, и превозносят. Споры о достоинствах и недостатках этого инструмента не утихают и, вероятно, не утихнут в ближайшее время. Однако часто мы упускаем из виду, что критика любого языка или платформы основывается на возникающих проблемах, зависящих от того, как мы эти платформы используем. Вне зависимости от того, насколько Node.js усложняет написание безопасного кода и облегчает его распараллеливание, платформа существует уже довольно давно, и на ней создано огромное количество надёжных и сложных веб-сервисов. Все они хорошо масштабируются и на практике доказали свою устойчивость.

Но, как и любая платформа, Node.js не застрахован от ошибок самих разработчиков. В одних случаях падает производительность, в других — система становится практически непригодной к использованию. И в этом посте я хотел бы рассмотреть 10 наиболее частых ошибок, которые делают разработчики с недостаточным опытом работы с Node.js.

Ошибка первая: блокирование цикла событий


JavaScript в Node.js (как и в браузере) обеспечивает однопоточную среду. Это означает, что две или больше частей вашего приложения одновременно выполняться не могут. Распараллеливание осуществляется за счет асинхронной обработки операций ввода/вывода. Например, запрос Node.js к базе данных за каким-либо документом дает возможность Node.js уделить внимание другой части приложения:

// Пытаемся извлечь данные пользователя из базы данных. С этого момента Node.js свободен для выполнения других частей кода..
db.User.get(userId, function(err, user) {
	// .. до тех пор, пока данные пользователя не будут извлечены здесь
})



Однако кусок занимающего процессор кода способен заблокировать цикл событий, заставив тысячи подключённых клиентов ожидать завершения своего выполнения. В качестве примера подобного кода можно привести попытку сортировки очень большого массива:

function sortUsersByAge(users) {
	users.sort(function(a, b) {
		return a.age < b.age ? -1 : 1
	})
}

Вызов функции sortUsersByAge вряд ли создаст проблемы в случае с небольшим массивом. Но при работе с большим массивом это катастрофически снизит общую производительность. Проблем может и не возникнуть, если данная операция крайне необходима, и вы уверены, что никто другой не ожидает цикла событий (скажем, если вы делаете инструмент, запускаемый из командной строки, и асинхронность выполнения не нужна). Но для Node.js-сервера, обслуживающего тысячи клиентов одновременно, такой подход недопустим. Если этот массив пользователей извлекается непосредственно из базы данных, то лучшим решением было бы извлечь его уже отсортированным. Если цикл событий блокируется циклом вычисления общего результата большого количества финансовых транзакций, то эту работу можно делегировать какому-нибудь внешнему исполнителю, чтобы не блокировать цикл событий.

К сожалению, не существует серебряной пули для решения проблем этого типа, и в каждом случае нужен индивидуальный подход. Главное — не перегружать процессор в рамках выполнения инстанса Node.js, который параллельно работает с несколькими клиентами.

Ошибка вторая: вызов колбэка более одного раза


Работа JavaScript базируется на колбэках. В браузерах события обрабатываются путём передачи ссылок на функции (зачастую, анонимные), которые действуют как колбэки. Раньше в Node.js колбэки были единственным способом связи асинхронных частей кода между собой, пока не были внедрены обещания (promises). Однако колбэки всё ещё используются, и многие разработчики пакетов по-прежнему обращаются к ним при проектировании своих API. Частая ошибка — вызов колбэка более одного раза. Обычно метод, который делает что-то асинхронно, последним аргументом ожидает функцию, которую он вызовет после завершения своей асинхронной задачи:

module.exports.verifyPassword = function(user, password, done) {
	if(typeof password !== ‘string’) {
		done(new Error(‘password should be a string’))
		return
	}

	computeHash(password, user.passwordHashOpts, function(err, hash) {
		if(err) {
			done(err)
			return
		}
		
		done(null, hash === user.passwordHash)
	})
}

Обратите внимание на оператор возврата после каждого вызова “done”, за исключением последнего. Дело в том, что вызов колбэка не прерывает выполнение текущей функции. Если закомментировать первый “return”, то передача этой функции пароля, который не является строкой, приведет к вызову “computeHash”. И в зависимости от дальнейшего сценария работы “computeHash”, “done” может вызываться многократно. Любой посторонний пользователь, воспользовавшийся этой функцией, может быть застигнут врасплох вызовом колбэка несколько раз.

Чтобы избежать этой ошибки, достаточно проявлять бдительность. Некоторые разработчики взяли за правило добавлять ключевое слово “return” перед каждым вызовом колбэка:

if(err) {
	return done(err)
}

Во многих асинхронных функциях возвращаемое значение не важно, так что этот подход зачастую позволяет избежать многократного вызова колбэка.

Ошибка третья: глубоко вложенные колбэки


Это проблему часто называют «Callback Hell». Хотя само по себе это не является ошибкой, но может стать причиной того, что код быстро выйдет из-под контроля:

function handleLogin(..., done) {
	db.User.get(..., function(..., user) {
		if(!user) {
			return done(null, ‘failed to log in’)
		}
		utils.verifyPassword(..., function(..., okay) {
			if(okay) {
				return done(null, ‘failed to log in’)
			}
			session.login(..., function() {
				done(null, ‘logged in’)
			})
		})
	})
}



Чем сложнее задача, тем глубже может быть вложенность. Это приводит к неустойчивому и трудночитаемому коду, плохо поддающемуся сопровождению. Один из способов решения этой проблемы — выделить каждую задачу в отдельную функцию, а затем связать их. В то же время, многие считают, что лучше всего использовать модули, реализующие паттерны асинхронного JavaScript, такие как Async.js:

function handleLogin(done) {
	async.waterfall([
		function(done) {
			db.User.get(..., done)
		},
		function(user, done) {
			if(!user) {
			return done(null, ‘failed to log in’)
			}
			utils.verifyPassword(..., function(..., okay) {
				done(null, user, okay)
			})
		},
		function(user, okay, done) {
			if(okay) {
				return done(null, ‘failed to log in’)
			}
			session.login(..., function() {
				done(null, ‘logged in’)
			})
		}
	], function() {
		// ...
	})
}

Помимо “async.waterfall” в Async.js содержится и ряд других функций, обеспечивающих асинхронное выполнение JavaScript. Для краткости здесь представлен довольно простой пример, но в реальности, зачастую, все гораздо хуже.

Ошибка четвёртая: рассчитывать, что колбэки будут выполняться синхронно


Асинхронное программирование с колбэками не являются чем-то необычным для JavaScript и Node.js. Другие языки приучили нас к предсказуемости порядка выполнения, когда два выражения выполняются последовательно, одно за другим, если нет никаких особых инструкций для перехода между ними. Но даже в этом случае мы зачастую ограничены условными операторами, циклами и вызовами функций.

Однако в JavaScript колбэки позволяют сделать так, что некая функция может не выполняться до тех пор, пока не будет завершена какая-то задача. Здесь функция будет выполняться без остановки:

function testTimeout() {
	console.log(“Begin”)
	setTimeout(function() {
		console.log(“Done!”)
	}, duration * 1000)
	console.log(“Waiting..”)
}

При вызове функции “testTimeout” сначала будет выведено “Begin”, затем “Waitng”, а примерно через секунду — “Done!”. Если что-то должно быть сделано после вызова колбэка, то оно должно быть вызвано в самом колбэке.

Ошибка пятая: присвоение “exports” вместо “module.exports”


Node.js работает с каждым файлом как с маленьким изолированным модулем. Допустим, ваш пакет содержит два файл a.js и b.js. Чтобы b.js мог получить доступ к функциональности из a.js, последний должен экспортировать эту функциональность путём добавления свойств объекту “exports”:

// a.js
exports.verifyPassword = function(user, password, done) { ... }

Если это сделано, то любой запрос a.js, вернет объект с функцией “verifyPassword” в свойствах:

// b.js
require(‘a.js’) // { verifyPassword: function(user, password, done) { ... } } 

А если нам нужно экспортировать эту функцию напрямую, а не как свойство какого-либо объекта? Мы можем сделать это, переопределив переменную “exports”, но главное не обращаться к ней, как к глобальной переменной:

/ a.js
module.exports = function(user, password, done) { ... }

Обратите внимание на “exports” в качестве свойства объекта “module”. Разница между “module.exports” и “exports” очень велика, и непонимание этого приводит к затруднениям у начинающих Node.js-разработчиков.

Ошибка шестая: генерация ошибок внутри колбэков


В JavaScript есть такое понятие, как исключение. Подражая синтаксису почти всех традиционных языков программирования, в которых тоже есть обработка исключений, JavaScript может генерировать и перехватывать исключения с помощью try-catch-блоков:

function slugifyUsername(username) {
	if(typeof username === ‘string’) {
		throw new TypeError(‘expected a string username, got '+(typeof username))
	}
	// ...
}

try {
	var usernameSlug = slugifyUsername(username)
} catch(e) {
	console.log(‘Oh no!’)
}

Однако в случаях асинхронного выполнения try-catch будет работать не так, как вы ожидаете. Например, если с помощью большого try-catch-блока вы попытаетесь защитить внушительный кусок кода с многочисленными асинхронными сегментами, то это может и не сработать:

try {
	db.User.get(userId, function(err, user) {
		if(err) {
			throw err
		}
		// ...
		usernameSlug = slugifyUsername(user.username)
		// ...
	})
} catch(e) {
	console.log(‘Oh no!’)
}

Если колбэк, переданный в “db.User.get”, будет вызван асинхронно, то try-catch-блок не сможет перехватить генерируемые в колбэке ошибки, поскольку он будет выполнен в другом контексте, отличном от контекста try-catch. Ошибки в Node.js можно обрабатывать по-разному, но необходимо придерживаться одного шаблона для аргументов всех колбэков function (err, …) — первым аргументом в каждом колбэке нужно ожидать ошибку, если таковая произойдет.

Ошибка седьмая: предполагать, что все числа целочисленные


В JavaScript нет целочисленного типа данных, здесь все числа — с плавающей запятой. Вы можете посчитать, что это не проблема, поскольку не так часто встречаются числа достаточно большие, чтобы возникли проблемы из-за ограничений плавающей запятой. Это заблуждение. Поскольку числа с плавающей запятой могут содержать целочисленные представления только до определённого значения, его превышение при любом вычислении сразу приводит к проблемам. Как ни странно, это выражение в Node.js расценивается как верное:

Math.pow(2, 53)+1 === Math.pow(2, 53)

Странности с числами в JavaScript на этом не заканчиваются. Несмотря на то, что это числа с плавающей запятой, с ними работают операторы, предназначенные для целочисленных данных:

5 % 2 === 1 // true
5 >> 1 === 2 // true

Однако в отличие от арифметических, побитовые операторы и операторы сдвига работают только с последними 32 битами подобных больших «целочисленных». Например, если сдвинуть “Math.pow(2, 53)” на 1, то результат всегда будет равен 0. Если применить поразрядное ИЛИ, то тоже будет 0.

Math.pow(2, 53) / 2 === Math.pow(2, 52) // true
Math.pow(2, 53) >> 1 === 0 // true
Math.pow(2, 53) | 1 === 0 // true

Скорее всего, вы редко сталкиваетесь с большими числами, но когда такое случится, воспользуйтесь одной из многочисленных библиотек, выполняющих точные математические операции с большими числами. Например, node-bigint.

Ошибка восьмая: игнорирование преимуществ потоковых API


Допустим, вам нужно создать небольшой прокси-сервер, который обрабатывает ответы при запросе каких-либо данных с другого сервера. Скажем, для работы с изображениями с Gravatar:

var http = require('http')
var crypto = require('crypto')

http.createServer()
.on('request', function(req, res) {
	var email = req.url.substr(req.url.lastIndexOf('/')+1)
	if(!email) {
		res.writeHead(404)
		return res.end()
	}

	var buf = new Buffer(1024*1024)
	http.get('http://www.gravatar.com/avatar/'+crypto.createHash('md5').update(email).digest('hex'), function(resp) {
		var size = 0
		resp.on('data', function(chunk) {
			chunk.copy(buf, size)
			size += chunk.length
		})
		.on('end', function() {
			res.write(buf.slice(0, size))
			res.end()
		})
	})
})
.listen(8080)

В данном примере мы берём изображение с Gravatar, читаем его в Buffer и отправляем в качестве ответа на запрос. Не самая плохая схема, поскольку эти изображения невелики. А если нужно проксировать контент гигабайтных размеров? Лучше использовать такой метод:

http.createServer()
.on('request', function(req, res) {
	var email = req.url.substr(req.url.lastIndexOf('/')+1)
	if(!email) {
		res.writeHead(404)
		return res.end()
	}

	http.get('http://www.gravatar.com/avatar/'+crypto.createHash('md5').update(email).digest('hex'), function(resp) {
		resp.pipe(res)
	})
})
.listen(8080)

Здесь мы берём изображение и просто транслируем в качестве ответа клиенту, без считывания целиком в буфер.

Ошибка девятая: использование Console.log для отладки


Console.log позволяет выводить в консоль что угодно. Передайте ему объект, и он выведет в консоль литерал JavaScript-объекта. Console.log принимает любое количество аргументов и выводит их, аккуратно разделив пробелами. Многие разработчики с удовольствием пользуются этим инструментом для отладки, однако рекомендуется не использовать “console.log” в реальном коде. Избегайте “console.log” даже в закомментированных строках. Лучше воспользуйтесь какой-нибудь специально написанной для этого библиотекой вроде debug. С помощью таких библиотек можно легко включать и отключать режим отладки при запуске приложения. Например, при использовании “debug”, если вы не установите соответствующую переменную окружения DEBUG, то отладочная информация не попадет в терминал:

// app.js
var debug = require(‘debug’)(‘app’)
debug(’Hello, %s!’, ‘world’)

Чтобы включить режим отладки, достаточно просто запустить этот код, присвоив переменной окружения DEBUG значение “app” или “*”:

DEBUG=app node app.js

Ошибка десятая: не использование программ-диспетчеров


Вне зависимости от того, выполняется ли ваш код в продакшене или в вашем локальном окружении, крайне рекомендуется использовать программу-диспетчер. Многие опытные разработчики считают, что код должен «падать» быстро. Если возникает неожиданная ошибка, не пытайтесь обрабатывать ее, позвольте программе упасть, чтобы диспетчер перезапустил ее в течение нескольких секунд. Конечно, это далеко не всё, что умеют делать диспетчеры. Например, можно настроить перезапуск программы в случае изменения каких-то файлов, и многое другое. Это существенно облегчает процесс разработки на Node.js. Можно посоветовать следующие диспетчеры:


У каждого из них свои плюсы и минусы. Кто-то хорошо работает одновременно с несколькими приложениями на одной машине, какие-то лучше справляются с логированием. Но если вы хотите начать использовать диспетчер, то выбирайте любой из предложенных.

Заключение


Некоторые из описанных ошибок могут иметь разрушительный эффект для вашей программы, некоторые могут стать причиной разочарований при реализации простейших вещей. Хоть Node.js достаточно прост для того, чтобы новичок смог начать с ним работать, есть много моментов, в которых легко напортачить. Если вы знакомы с другими языками программирования, то какие-то из этих ошибок вам могут быть известны. Но именно эти 10 ошибок характерны для начинающих Node.js-разработчиков. К счастью, их довольно легко избежать. Я надеюсь, что эта небольшая статья поможет начинающим разработчикам писать стабильные и эффективные приложения для всех нас.
Автор: @ZaValera Mahmud Ridwan

Комментарии (45)

  • +1
    По поводу module.exports vs. exports — можно просто делать везде module.exports:

    function a() {
    ///
    }

    function b() {
    ///
    }

    module.exports = {
    a: a,
    b: b
    };
    • +1
      Я так понял, что речь тут о том, что при присвоении exports = foo, exports перестает быть ссылкой на module.exports.

      Это можно объяснить на таком примере:
      var foo = { bar: {} };
      var bar = foo.bar;
      
      console.log(foo.bar); // {}
      
      bar.baz = 123;
      console.log(foo.bar); // { baz: 123 }
      
      foo.bar = 123;
      console.log(foo.bar); // 123
      
      bar = 456;
      console.log(foo.bar); // 123
      


      Конечно можно всегда писать module.exports =…, но лучше понимать в чем дело.
  • 0
    По поводу п. 1. Для ноды достаточно просто писать расширения, чтобы разгрузить основной поток и выполнять тяжёлые вычисления на c++ в отдельных потоках.
  • +22
    Это не 10 основных ошибок, а 10 азов.
  • –4
    Ошибкой является рассуждение о callback hell без вообще какого бы то ни было упоминания посулов (promise), и безальтернативная рекомендация async.js.
    • 0
      Рассуждение о callback hell странно слышать при наличии флага --harmony
  • +4
    Если честно, то звучит как «основные проблемы Node.js в выбранном языке и его практиках»
  • +4
    Ошибка №0: долбить зубилом по ноуту
    Речь о КПДВ, если что.
  • 0
    А есть способ, чтобы чейнить функции в async.js, а не передавать их в виде массива?
    Чтобы не
    async.waterfall([ func, func, func], func)
    а
    async.waterfall()
    .next(func(){})
    .next(func(){})
    .next(func(){})
    .fail(func(){})
    • +1
      Promises. Например Q или нативные.
      • +1
        Это понятно. Мой вопрос был про async. Было бы замечательно узнать о чём-то вроде www.npmjs.com/package/async-chained
    • 0
      А чем плох массив? Можно просто использовать именованный и добавлять функции push'ами:
      ar.push(func(){});
      ar.push(func(){});
      ar.push(func(){});
      ar.push(func(){});
      async.waterfall(ar,func);
      В вашем синтаксисе я не понял, в какой момент цепочка должна уйти на запуск — после добавления fail'а?
      • 0
        И с неименованным можно:
        async.waterfall([]
            .concat(func(){})
            .concat(func(){})
            .concat(func(){}),
         func(){}
        )
        
  • +3
    Вы зря в примерах приводите async.js. Это все таки не стандарт, и даже не эффективное решение. Лучше сразу приводите примеры на промисах, тем более они уже в stable версии из коробки идут, если уж это для начинающих материал.
    • 0
      Да, описание решения на промисах было бы куда уместнее. Видимо автор решил, что если человек заметит у себя callback hell, то сможет найти подходящее решение сам.
    • 0
      Т.е. сейчас node.js поддерживает промисы нативно и можно отказаться от использования подобных либ https://github.com/kriskowal/q?
      • +1
        Да, текущая версия ноды 0.12.2 (Stable) поддерживает промисы нативно. 0.10.38 (Maintenance), если я не ошибаюсь, не поддерживает. Также вы можете попробовать IO.js
  • 0
    Большинство ошибок относится к асинхронной модели программирования, а не только к Node.js. И они не являются основными. Программист, допускающий их, не знает устройство своего языка.
    • +3
      Большинство начинающих программистов путаются в тонкостях языка и скорее не из-за незнания, а из-за неопытности. Статьи подобного рода помогают избежать лишней путаницы.
      • 0
        Из-за незнания. Именно поэтому лучше читать статьи подобные вот этой: http://habrahabr.ru/post/222761/

        Вместо статьи где размазаны основные идеи на 100 предложений вместо списка на 10 строк (хороших практик):

        1. из базы вызываем массив отсортированным
        2. return callback();
        3. используем async.waterfall или Promise
        4. колбэки несинхронны
        5. всегда module.exports
  • 0
    Как автор прокомментирует эту статью 12factor.net/logs применительно к 9 ошибке?
    • +2
      Вы можете спросить это у автора напрямую www.toptal.com/resume/mahmud-ridwan.
      А как проблема использования console.log при отладке связана с логированием работы приложения?
  • –12
    Статья слизана с: www.airpair.com/node.js/posts/top-10-mistakes-node-developers-make
    Желательно указывать ссылку на оригинал.
    • +1
      Это перевод. У заголовка есть соответствующая отметка, а внизу — ссылка на оригинал.
      • –17
        >>ссылка на оригинал.
        Вот за это люди и не уважают рунет. Перепечатка на перепечатке и даже оригинал не в состоянии указать. Минусите дальше хомяки. А лучше совсем забаньте меня, меня с вас тошнит.

        Люди не способные проселдить оригинал или отдать дань уважения автору, не заслуживают уважения.
        • +1
          Читайте внимательнее. Это разные статьи на одну тему. Видно по составу указанных 10 пунктов.
          • –9
            Это особенно заметно по переставленным пунктам. То, что Mahmud Ridwan слизал статью переставив пункты не делает ему чести. Но то, что вы пытаетесь это игнорировать позорит вас ещё больше. Alexandru Vladutu написал статью 4 месяца назад, Mahmud Ridwan слизал ее, месяца даже не прошло.

            Игнорировать неугодные факты похоже входит у некоторых людей в привычку.

            Хомяки фас!
            • –8


              Год назад я сомневался, но теперь я вижу. Вы заслуживаете цензуру в полном объеме!
              • +2
                А цензура-то причём?
                • –8
                  Отрицательная карма — это цензура, бан — тоже цензура. С учетом того, что статья «краденная» и нет отсылки на оригинал, а за указание этого факта я получаю «цензурную карму» вы должны радоваться наличию цензуры, а не ныть как год назад, что вас чем-то обижают. Вы заслужили цензуру.

                  Ставить отрицательную карму и выступать против цензуры — лицемерие в чистом виде!
                  • +1
                    А вы хотя бы ознакомились со статьями, перед тем, как громко кричать о том, что кто-то у кого-то украл. Даже при беглом просмотре видно, что в статьях есть общие моменты, а есть индивидуальные. Да и при обсуждении одних и тех же проблем, у авторов различные объяснения и примеры. С каких пор обсуждение одних и тех же вопросов стало плагиатом? Можете написать о своем видение основных ошибок при разработке на ноде, мы все с радостью почитаем!
                    • –8
                      Бессмысленно отрицать очевидное. Именно поэтому вы и заслужили цензуру. Вместо простого признания ошибки и указания оригинального источника хомяки сражаются с несогласным… В чём проблема? Просто забаньте.
                      • +6
                        Потому что мы злодеи — поощряем страдания того, кто сам погрязает в собственном безумии.
            • +3
              А прочитать обе статьи слабо? Ничего что пункты РАЗНЫЕ, а не переставленные?

              Кстати, где Вы взяли слово «хомяки» Прямо как школьник. Или лавры старых интернет-задротов покоя не дают? Так мода на задротство прошла.
              • –3
                Извините, персонально вы из детсада. ;)

                Я прочитал обе, обе статьи слабые, но эта самая жуткая из-за картинок нарисованных человеком у которого возможно была насильственная лоботомия.

                ПС: Ничто в этом мире не заставит меня поменять своё мнение, что статья вымучена, чтобы спереть 10 ошибок. Если вам это не нравиться, то можете рвать на нижней части спины волосы сколько влезет, я все равно буду считать что мсье «хомяк». Иначе бы не написал коммент через 4 дня, да к тому же когда уже итак все насрали в карму. Собственно вы просто подтвердили, что принадлежите к стаду.
                • 0
                  Подтвердить свои слова готовы?
                  Давайте по номерам, какие ошибки спёрты? Со ссылками, откуда они спёрты (естественно, с номером в первоисточнике).
                  • +2
                    Но проблемо.

                    1 nodemon (Ошибка десятая: не использование программ-диспетчеров)
                    2 Blocking the event loop (Ошибка первая: блокирование цикла событий)
                    3 Executing a callback multiple times (Ошибка вторая: вызов колбэка более одного раза)
                    4 Callback Hell (Ошибка третья: глубоко вложенные колбэки)
                    5 Creating big monolithic applications (Ошибка четвёртая: рассчитывать, что колбэки будут выполняться синхронно)
                    6 Poor logging
                    7 No tests
                    8 Not using static analysis tools
                    9 Zero monitoring or profiling
                    10 Debugging with console.log (Ошибка девятая: использование Console.log для отладки)

                    Притянутое за уши (высосано для отличий):
                    Ошибка пятая: присвоение “exports” вместо “module.exports” — (в js разве по другому? при чем тут nodejs? Таких ошибок можно еще море навыдумывать и все будут в js)
                    Ошибка седьмая: предполагать, что все числа целочисленные (туда же, привет js)
                    Ошибка восьмая: игнорирование преимуществ потоковых API (шо, nodejs?)

                    Про это даже писать не стоило. Это из разряда КЭП.

                    Ошибка шестая: генерация ошибок внутри колбэков () — единственная отличительная ошибка. Про неё стоило написать подробно, но автор не способен похоже. Игнорировать логирование, вообще позор. Это наверное самая страшная ошибка.

                    Большинство «читателей» темы похоже не знакомы с образованием. Если написали науч. работу и не упомянули оригинал, вас носом ткнут за отсутствие отсылки на оригинал. Если защищаете автора статьи, то он не чист на руку, если защищаете автора перевода, то тоже всё печально, если он не в состоянии сравнить статьи. Гордость не позволяет?

                    Минусаторы показывают, насколько они не в состоянии анализировать. Мне на минусы плевать, мне тут похоже делать совсем нечего.

                    Ну, пожалуй хватит. Это было скучно, но забавно для наблюдения рефлексии. ;)

                    Думать не пробовали?
                    • 0
                      Минусаторы сбежали из ВКонтакта. Мы здесь не о них. Плюсы и минусы ставят за «котиков» — это я уже заметил.

                      Совпадение на 60%. Согласен, я был не прав.
                      Статья — не украдена, но такой большой процент заимствования нужно сопровождать ссылкой.

                      Учитывая качество статьи (пресловутая притянутость за уши), я не исключаю, что автор мог тупо не знать о другой статье.

                      Ошибки
                      2 Blocking the event loop (Ошибка первая: блокирование цикла событий)
                      3 Executing a callback multiple times (Ошибка вторая: вызов колбэка более одного раза)
                      5 Creating big monolithic applications (Ошибка четвёртая: рассчитывать, что колбэки будут выполняться синхронно)

                      сами по себе достаточно очевидны, могли быть выявлены независимо.

                      Остаётся 30% — всё равно много.

                      UPD:
                      Кстати, с трудом себе представляю человека, способного совершить ошибки
                      3 Executing a callback multiple times (Ошибка вторая: вызов колбэка более одного раза)
                      5 Creating big monolithic applications (Ошибка четвёртая: рассчитывать, что колбэки будут выполняться синхронно)
                      • 0
                        >>Кстати, с трудом себе представляю человека, способного совершить ошибки
                        >>5 Creating big monolithic applications (Ошибка четвёртая: рассчитывать, что колбэки будут выполняться синхронно)
                        На эти грабли персонально наступил :) Не в таком примере как выше, но суть та же.
  • –6
    >>мы злодеи
    Да, да, злые диванные боевые хомяки. ;)
    • +2
      Спичка вспыхнула и сгорела… чего вы так взъелись? Одного сообщения в стиле «негодую» хватит, ненужно троллинг разводить
    • 0
      а Вы у нас Д'Артаньян, я так понимаю? Добро пожаловать.
  • 0
    Math.pow(2, 53) | 1 === 0 // true

    Вот здесь не совсем понятно… Почему здесь будет true? OR c 1 всегда должен давать 1 не зависимо от другого аргумента. Консоль хрома выдает результат:
    Math.pow(2, 53) | 1 === 0
    0
    Math.pow(2, 53) | 1
    1

    Или это какая-то особенность именно Node.js?
    • 0
      Этот код равносилен Math.pow(2, 53) | (1 === 0) , а нужно (Math.pow(2, 53) | 1) === 0

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Самое читаемое Разработка