NodeJS 开发者的 10 个常见错误

NodeJS 开拓者的 10 个何奇之有错误

2015/06/16 · CSS · 1
评论

本文由 伯乐在线 –
刘健超-J.c
翻译,黄利民
校稿。未经许可,防止转发!
爱尔兰语出处:www.toptal.com。接待参加翻译组。

自 Node.js
公之于众的那一刻,就陪伴着表扬和研究的响动。这几个争辨仍在不停,何况并不会不慢藏形匿影。而大家平日忽视掉这一个纠纷发生的缘由,每一个编制程序语言和平台都是因一些问题而受到商量,而那些难点的发出,是在于大家如何行使这几个平台。不管有多难工夫写出平安的
Node.js
代码,或有多轻松写出高并发的代码,该平台早原来就有一定长后生可畏段时间,并已被用来树立一个多少一点都不小、稳健和老成的
web 服务器。这个 web 服务器伸缩性强,而且它们经过在 Internet
上稳固的运营时刻,注明自个儿的安身立命。

然则,像此外平台相似,Node.js
轻巧因开拓者难题而饱受争辨。一些破绽超多会回降品质,而别的一些主题材料会让
Node.js 直接崩溃。在此篇文章里,我们将会聊意气风发聊关于 Node.js 新手的 11个常犯错误,并让他俩知晓什么样幸免这个不当,进而成为一名 Node.js 高手。

图片 1

错误 #1:梗塞事件循环

JavaScript 在 Node.js (就如在浏览器相像)
提供单线程推行意况。那意味你的程序无法同期推行两有个别代码,但能通过 I/O
绑定异步回调函数达成产出。譬喻:三个出自Node.js
的号令是到数据库引擎获取一些文书档案,在这里同期允许 Node.js
专心于应用程序此外一些:

JavaScript

// Trying to fetch an user object from the database. Node.js is free to
run other parts of the code from the moment this function is invoked..
// 尝试从数据库中获得二个客商对象。在这里个函数试行的一刻,Node.js
有空去运作代码此外一些.. db.User.get(userId, function(err, user) { // ..
until the moment the user object has been retrieved here // ..
直到用户对象检索到这边的那一刻 })

1
2
3
4
5
6
// Trying to fetch an user object from the database. Node.js is free to run other parts of the code from the moment this function is invoked..
// 尝试从数据库中获取一个用户对象。在这个函数执行的一刻,Node.js 有空去运行代码其它部分..
db.User.get(userId, function(err, user) {
// .. until the moment the user object has been retrieved here
        // .. 直到用户对象检索到这里的那一刻
})

图片 2

可是,具有总括密集型代码的 Node.js
实例被风流罗曼蒂克体系顾客端同偶然间连接实行时,会招致短路事件循环,并使拥有客商端处于等候响应状态。计算密集型代码,包涵尝试给三个非常的大数组举办排序操作和平运动转贰个卓绝长的循环等。举个例子:

JavaScript

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

1
2
3
4
5
function sortUsersByAge(users) {
users.sort(function(a, b) {
return a.age > b.age ? -1 : 1
})
}

依据小 “users” 数组施行 “sortUserByAge”
函数,大概没什么难点,当基于宏大数组时,会严重影响总体品质。要是在一定要这么操作的状态下,你必需确认保障程序除了等待事件循环而别无她事(举例,用
Node.js
创设命令行工具的大器晚成有个别,整个东西一块运营是没难点的卡塔 尔(英语:State of Qatar),然后那或许没难题。不过,在
Node.js
服务器实例尝试同期服务广大个客户的气象下,那将是二个灭绝性的题目。

万风度翩翩客商数组是从数据库检索出来的,有个消除办法是,先在数据库中排序,然后再一向找出。借使因急需总括宏大的金融交易历史数据总和,而招致拥塞事件循环,那足以成立额外的worker
/ queue 来防止堵塞事件循环。

正如你所见到的,那从没新技艺来减轻那类 Node.js
难题,而每一种情景都急需独自管理。而基本消除思路是:不要让 Node.js 实例的主线程试行 CPU 密集型职业 –
客商端同一时候链接时。

错误 #2:调用回调函数多于一回

JavaScript
平素都以借助于回调函数。在浏览器中,处监护人件是经过调用函数(平时是无名的卡塔 尔(阿拉伯语:قطر‎,那几个动作就像回调函数。Node.js
在引入 promises 在此以前,回调函数是异步成分用来互相连接对方的唯一方法
。今后回调函数仍被使用,况兼包开垦者依然围绕着回调函数设计
APIs。叁个关于选择回调函数的置之不理 Node.js 难点是:不仅仅叁次调用。平时状态下,叁个包提供三个函数去异步管理部分事物,设计出来是期望有三个函数作为最终三个参数,当异步任务达成时就能被调用:

JavaScript

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) }) }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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卡塔 尔(阿拉伯语:قطر‎,而结尾三个 “done”
则可省略再次来到语句。那是因为调用回调函数后,并不会自行终止近期执行函数。要是第三个“return” 注释掉,然后给那些函数字传送进二个非字符串密码,以致 “computeHash”
如故会被调用。那决议于 “computeHash” 如哪处理那样少年老成种处境,“done”
恐怕会调用多次。任何一个人在别处使用这些函数大概会变得措手不如,因为它们传进的该回调函数被频频调用。

只要当心就可以防止这些 Node.js 错误。而一些 Node.js
开拓者养成贰个习认为常是:在各类回调函数调用前增加叁个 return 关键字。

JavaScript

if(err) { return done(err) }

1
2
3
if(err) {
return done(err)
}

对于众多异步函数,它的重临值差不离是虚幻的,所以该措施能令你很好地防止那么些标题。

错误 #3:函数嵌套过深

函数嵌套过深,时常被誉为“回调函数鬼世界”,但那实际不是 Node.js
自个儿难题。不过,那会招致贰个难题:代码非常快失去调控。

JavaScript

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’) }) }) }) }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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’)
})
})
})
}

图片 3

任务有多复杂,代码就有多糟糕。以这种措施嵌套回调函数,大家十分轻巧就能遭受难点而咽气,並且难以阅读和爱惜代码。风流倜傥种代替形式是以函数注明那个义务,然后将它们连接起来。固然,有意气风发种最干净的主意之生机勃勃(有争辩的卡塔尔国是应用 Node.js 工具包,它非常处理异步 JavaScript
方式,比方 Async.js :

JavaScript

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() { // … }) }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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
提供了成千上万别样函数来扼杀分歧的异步格局。为了简洁,大家在此边运用二个较为轻易的案例,但实际上处境每每更糟。

错误 #4:期望回调函数以合营格局运转

异步程序的回调函数并非 JavaScript 和 Node.js
唯有的,但它们是促成回调函数流行的缘由。而对于任何编制程序语言,大家不识不知地认为实施顺序是一步接一步的,如多个语句将会实践完第一句再施行第二句,除非这两个语句间有一个醒目标跳转语句。尽管那样,它们日常局限于法规语句、循环语句和函数调用。

只是,在 JavaScript
中,回调某些特定函数大概并不会应声运营,而是等到职责到位后才运维。下边例子正是直到未有别的职责,当前函数才运维:

JavaScript

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

1
2
3
4
5
6
7
function testTimeout() {
console.log(“Begin”)
setTimeout(function() {
console.log(“Done!”)
}, duration * 1000)
console.log(“Waiting..”)
}

您会潜心到,调用 “test提姆eout” 函数会首先打字与印刷 “Begin”,然后打字与印刷“Waiting..”,紧接大约风流倜傥秒后才打字与印刷 “Done!”。

别的二个内需在回调函数被触发后进行的事物,都要把它位于回调函数内。

错误 #5:用“exports”,而不是“module.exports”

Node.js
将各类文件视为二个孤立的小模块。假设您的包(package卡塔 尔(阿拉伯语:قطر‎含有七个文本,大概是
“a.js” 和 “b.js”。因为 “b.js” 要博取 “a.js” 的功用,所以 “a.js”
必需经过为 exports 对象增添属性来导出它。

JavaScript

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

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

当那样操作后,任何引进 “a.js” 模块的文件将会获取三个分包属性方法
“verifyPassword” 的对象:

JavaScript

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

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

然则,如若大家想直接导出这一个函数,实际不是用作有些对象的习性呢?大家能通过覆盖
exports 对象来完毕这些目标,但我们不可能将它正是三个全局变量:

JavaScript

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

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

小心,我们是怎么将 “exports” 作为 module
对象的五特性质。在这里处精晓 “module.exports” 和 “exports”
之间分歧是老大重大的,何况那平常会促成 Node.js 开拓新手们发出挫败感。

错误 #6:在回调函数内抛出错误

JavaScript 有个“万分”概念。相当管理与超越百分之二十古板语言的语法相仿,举例 Java
和 C++,JavaScript 能在 try-catch 块内 “抛出(throw卡塔尔国” 和
捕捉(catch卡塔 尔(英语:State of Qatar)非常:

JavaScript

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!’) }

1
2
3
4
5
6
7
8
9
10
11
12
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
块内,而结果是:它不鲜明会运作。

JavaScript

NodeJS 开发者的 10 个常见错误。try { db.User.get(userId, function(err, user) { if(err) { throw err } //
… usernameSlug = slugifyUsername(user.username) // … }) } catch(e) {
console.log(‘Oh no!’) }

1
2
3
4
5
6
7
8
9
10
11
12
try {
db.User.get(userId, function(err, user) {
if(err) {
throw err
}
// …
usernameSlug = slugifyUsername(user.username)
// …
})
} catch(e) {
console.log(‘Oh no!’)
}

NodeJS 开发者的 10 个常见错误。设若回调函数 “db.User.get” 异步触发了,固然作用域里包含的 try-catch
块离开了上下文,依旧能捕捉那叁个在回调函数的抛出的荒诞。

那就是 Node.js
中怎么样管理错误的其它风度翩翩种办法。别的,有必不可缺信守全体回调函数的参数(err,
…卡塔 尔(阿拉伯语:قطر‎情势,全体回调函数的第4个参数期望是二个错误对象。

NodeJS 开发者的 10 个常见错误。错误 #7:以为数字是整型

数字在 JavaScript 中都是浮点型,JS
没有整型。你可能不能预料到那将是多个难题,因为数大到过量浮点型范围的图景并不布满。

JavaScript

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

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

噩运的是,在 JavaScript
中,这种关于数字的古怪情形远不仅仅于此。就算数字都以浮点型,对于上边的表明式,操作符对于整型也能健康运作:

JavaScript

5 >> 1 === 2 // true

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

唯独,不像算术运算符这样,位操作符和活动操作符只可以操作后 三十四位,就如 “整型” 数。比如,尝试位移 “Math.pow(2,53)” 1 位,会获取结果
0。尝试与 1 进行按位或运算,获得结果 1。

JavaScript

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

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

您也许比超少需求管理非常大的数,但借使你实在要拍卖的话,有为数不菲大整型库能对大型精度数完毕关键的数学生运动算,如
 node-bigint。

错误 #8:忽略了 Streaming(流) API 的优势

大家都在说想创立二个微型代理服务器,它能响应从别的服务器获取内容的央浼。作为二个案例,大家将创建二个供应
Gravatar
图像的迷你 Web 服务器:

JavaScript

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(”),
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)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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)

在这里个奇特例子中有一个 Node.js 难点,大家从 Gravatar
获取图像,将它读进缓存区,然后响应央求。那不是一个多么不佳的难题,因为
Gravatar
再次回到的图像而不是超级大。但是,想象一下,假设大家代理的剧情大小有数不胜数兆。那就有二个越来越好的措施了:

JavaScript

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(”),
function(resp) { resp.pipe(res) }) }) .listen(8080)

1
2
3
4
5
6
7
8
9
10
11
12
13
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)

这里,大家获得图像,并简短地因而管道响应给顾客端。绝不供给大家在响应从前,将全部内容读取到缓冲区。

错误 #9:把 Console.log 用于调试指标

在 Node.js 中,“console.log”
允许你向调整台打字与印刷大概拥有东西。传递三个对象给它,它会以 JavaScript
对象字面量的章程打字与印刷出来。它接纳任意三个参数,并以空格作为分隔符打印它们。有成千上万个理由让开采者很想用这么些来调度(debug卡塔尔自身的代码;然则,小编刚烈建议你制止在真的程序里使用
“console.log” 。你应当幸免在方方面面代码里使用 “console.log”
进行调节和测量检验(debug卡塔 尔(阿拉伯语:قطر‎,当没有要求它们的时候,应注释掉它们。相反,使用特意为调节和测量检验建设构造的库,如:debug。

当您起头编写制定应用程序时,那几个库能方便地开发银行和剥夺某行调节和测量检验(debug卡塔 尔(英语:State of Qatar)效能。譬如,通过不设置
DEBUG 遭受变量,能够幸免全部调节和测施行被打字与印刷到终极。使用它很简短:

JavaScript

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

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

为了运转调节和测施行,将意况变量 DEBUG 设置为 “app” 或
“*”,就能够简单地运转那么些代码了:

JavaScript

DEBUG=app node app.js

1
DEBUG=app node app.js

错误 #10:不应用项理程序

任凭你的 Node.js
代码运转在生育情状如故本地开垦条件,一个监督管理程序能很好地保管你的顺序,所以它是叁个十一分平价并值得具备的事物。开垦者设计和贯彻今世选用时日常推荐的三个超级试行是:急速退步,快速迭代。

假若爆发三个预料之外的大错特错,不要试图去管理它,而是让您的顺序崩溃,并有个监察和控制者在几秒后重启它。管理程序的利润不只有是重启崩溃的主次。这几个工具允许你重启崩溃的次序的同时,也同意文件发出改换时重启程序。那让开发Node.js 程序成为风度翩翩段更欢喜的心得。

有多数 Node.js 可用的田间管理程序。比如:

  • pm2
  • forever
  • nodemon
  • supervisor

具有那一个工具各有高低。一些方便在同一个机械里管理四个应用程序,而此外擅擅长日志管理。但是,假使您想起来采取那些程序,它们都以很好的精选。

总结

正如你所驾驭的那么,一些 Node.js
难点能对您的程序变成消亡性打击。而有的则会在您品尝成功最简单易行的东西时,让你发出挫败感。就算Node.js
的耗费门槛十分低,但它仍有比较轻便搞混之处。从其它编制程序语言转过来学习
Node.js 开辟者大概会遇见那么些标题,但这个错误在 Node.js
新手中也是不行大面积的。幸运的是,它们比较轻巧制止。笔者梦想那几个轻松指引能支持Node.js 生手写出更能够的代码,并为大家开辟出平安快速的软件。

打赏支持自个儿翻译愈来愈多好文章,多谢!

打赏译者

打赏支持小编翻译更加多好小说,多谢!

任选黄金时代种支付办法

图片 4
图片 5

1 赞 7 收藏 1
评论

有关作者:刘健超-J.c

图片 6

前端,在路上…
个人主页 ·
我的稿子 ·
19 ·
    

图片 7

发表评论

电子邮件地址不会被公开。 必填项已用*标注