>웹 프론트엔드 >프런트엔드 Q&A >nodejs에서 흔히 발생하는 상위 10가지 오류는 무엇입니까?

nodejs에서 흔히 발생하는 상위 10가지 오류는 무엇입니까?

青灯夜游
青灯夜游원래의
2022-01-13 17:14:282353검색

노드에서 흔히 발생하는 10가지 실수: 1. 이벤트 루프 차단 2. 콜백 함수를 여러 번 호출 3. 콜백 함수가 동기적으로 실행될 것으로 예상 5. "내보내기"에 값 할당 6, 콜백에서 오류가 발생합니다. 7. Think Number는 정수 데이터 형식입니다. 8. 스트리밍 API 등의 장점을 무시합니다.

nodejs에서 흔히 발생하는 상위 10가지 오류는 무엇입니까?

이 튜토리얼의 운영 환경: windows7 시스템, nodejs 버전 12.19.0, DELL G3 컴퓨터.

Node.js가 출시된 이후 많은 칭찬과 비판을 받았습니다. 이 논쟁은 계속될 것이며 조만간 끝나지 않을 것입니다. 이러한 논쟁에서 우리는 모든 언어와 플랫폼이 몇 가지 핵심 문제를 기반으로 비판을 받는다는 사실, 즉 이러한 플랫폼을 사용하는 방식을 간과하는 경우가 많습니다. Node.js로 안정적인 코드를 작성하는 것이 아무리 어렵고 동시성 코드를 작성하는 것이 얼마나 쉬운지에 관계없이 플랫폼은 한동안 존재해 왔으며 수많은 강력하고 복잡한 웹 서비스를 만드는 데 사용되었습니다. 이러한 웹 서비스는 확장성이 뛰어날 뿐만 아니라 인터넷에서 지속되는 시점까지 그 견고성을 입증합니다.

그러나 다른 플랫폼과 마찬가지로 Node.js도 개발자가 실수하기 쉽습니다. 이러한 오류 중 일부는 프로그램 성능을 저하시킬 수 있고 다른 오류는 Node.js를 사용할 수 없게 만들 수 있습니다. 이 글에서는 Node.js 초보자들이 저지르는 일반적인 실수와 이를 방지하는 방법을 살펴보겠습니다. <span class="pun">十种错误</span>

오류 1: 이벤트 루프 차단

Node.js의 JavaScript(브라우저와 마찬가지로)는 단일 스레드 환경을 제공합니다. 이는 프로그램이 두 가지를 동시에 실행하는 것이 아니라 I/O 집약적인 작업을 비동기식으로 처리하여 동시성을 갖게 된다는 것을 의미합니다. 예를 들어 Node.js가 일부 데이터를 얻기 위해 데이터베이스에 대한 요청을 시작하면 Node.js는 프로그램의 다른 부분에 집중할 수 있습니다.

// 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..	
db.User.get(userId, function(err, user) {	
    // .. until the moment the user object has been retrieved here	
})

그러나 수천 개의 클라이언트 연결이 있는 Node.js 인스턴스에서는 Small 섹션 CPU 집약적인 코드로 인해 이벤트 루프가 차단되어 모든 클라이언트가 기다리게 될 수 있습니다. CPU 집약적인 코드에는 거대한 배열 정렬, 긴 함수 실행 등이 포함됩니다. 예:

function sortUsersByAge(users) {	
    users.sort(function(a, b) {	
        return a.age &amp;lt; b.age ? -1 : 1	
    })	
}

작은 "users" 배열에서는 "sortUsersByAge" 메서드를 호출하는 데 문제가 없지만, 큰 배열에서는 전체 성능에 큰 영향을 미칩니다. 이런 종류의 작업을 수행해야 하고 이벤트 루프에서 대기 중인 다른 이벤트가 없는지 확인할 수 있는 경우(예를 들어 이것은 단지 Node.js 명령줄 도구일 뿐이며 모든 것이 동기적으로 작동하는지 상관하지 않습니다) ) 그렇다면 문제가 되지 않습니다. 그러나 Node.js 서버가 수천 명의 사용자에게 동시에 서비스를 제공하려는 상황에서는 문제가 발생할 수 있습니다.

이 사용자 배열을 데이터베이스에서 가져온 경우 이상적인 솔루션은 데이터베이스에서 정렬된 데이터를 가져오는 것입니다. 금융 거래 데이터의 과거 합계를 계산하는 루프에 의해 이벤트 루프가 차단되는 경우 이벤트 루프가 묶이는 것을 방지하기 위해 계산 루프를 이벤트 루프 외부 큐로 푸시해야 합니다.

보시다시피 이러한 유형의 오류를 해결할 수 있는 묘책은 없으며 각 상황에 대한 개별 솔루션이 있을 뿐입니다. 기본 아이디어는 동시 클라이언트 연결을 처리하는 Node.js 인스턴스에서 CPU 집약적인 작업을 수행하지 않는 것입니다.

오류 2: 콜백 함수를 여러 번 호출 중

JavaScript는 항상 콜백 함수에 의존해 왔습니다. 브라우저에서는 이벤트 개체에 대한 참조를 콜백 함수(일반적으로 익명 함수)에 전달하여 이벤트를 처리합니다. Node.js에서 콜백 함수는 Promise가 등장할 때까지 다른 코드와 비동기적으로 통신하는 유일한 방법이었습니다. 콜백 함수는 오늘날에도 여전히 사용되고 있으며 많은 개발자가 여전히 이를 중심으로 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)	
    })	
}

마지막 시간을 제외하고 모든 "완료" 메서드에는 호출된 후 반환 문이 되어야 합니다. 콜백 함수를 호출해도 현재 메서드의 실행이 자동으로 종료되지 않기 때문입니다. 첫 번째 return 문을 주석 처리한 다음 문자열이 아닌 비밀번호를 이 함수에 전달하면 결국 결국 computHash 메서드를 호출하게 됩니다. ComputeHash가 이 상황을 어떻게 처리하는지에 따라 "done" 함수가 여러 번 호출됩니다. 전달된 콜백 함수가 여러 번 호출되면 누구나 당황할 수 있습니다.

이 문제를 피하려면 주의하세요. 따라서 일부 Node.js 개발자는 콜백 함수를 호출하는 모든 문 앞에 return 키워드를 추가하는 습관을 개발했습니다.

if(err) {	
    return done(err)	
}

많은 비동기 함수에서 이 반환의 반환 값은 의미가 없으므로 이 작업은 단순히 이러한 실수를 피하기 위한 것입니다. .

오류 3: 깊게 중첩된 콜백 함수

깊게 중첩된 콜백 함수는 종종 "콜백 지옥"이라고 합니다. 그 자체로는 문제가 되지 않지만 코드가 빠르게 제어할 수 없게 될 수 있습니다.

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

越复杂的任务,这个的坏处就越大。像这样嵌套回调函数,我们的程序很容易出错,而且代码难以阅读和维护。一个权宜之计是把这些任务声明为一个个的小函数,然后再将它们联系起来。不过,(有可能是)最简便的解决方法之一是使用一个 Node.js 公共组件来处理这种异步 js,比如 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.js 还提供了很多类似“async.waterfall” 的方法去处理不同的异步场景。为了简便起见,这里我们演示了一个简单的示例,实际情况往往复杂得多。

(打个广告,隔壁的《ES6 Generator 介绍》提及的 Generator 也是可以解决回调地狱的哦,而且结合 Promise 使用更加自然,请期待隔壁楼主的下篇文章吧:D)

错误4:期待回调函数同步执行

使用回调函数的异步程序不只是 JavaScript 和 Node.js 有,只是它们让这种异步程序变得流行起来。在其他编程语言里,我们习惯了两个语句一个接一个执行,除非两个语句之间有特殊的跳转指令。即使那样,这些还受限于条件语句、循环语句以及函数调用。

然而在 JavaScript 里,一个带有回调函数的方法直到回调完成之前可能都无法完成任务。当前函数会一直执行到底:

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

你可能会注意到,调用“testTimeout” 函数会先输出“Begin”,然后输出“Waiting..”,紧接着几秒后输出“Done!”。

任何要在回调函数执行完后才执行的代码,都需要在回调函数里调用。

错误5:给“exports” 赋值,而不是“module.exports”

Node.js 认为每个文件都是一个独立的模块。如果你的包有两个文件,假设是“a.js” 和“b.js”,然后“b.js” 要使用“a.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 新手踩坑。

错误6:从回调里抛出错误

JavaScript 有异常的概念。在语法上,学绝大多数传统语言(如 Java、C++)对异常的处理那样,JavaScript 可以抛出异常以及在 try-catch 语句块中捕获异常:

function slugifyUsername(username) {	
    if(typeof username === ‘string’) {	
        throw new TypeError(‘expected a string username, got &#39;+(typeof username))	
    }	
    // ...	
}	
try {	
    var usernameSlug = slugifyUsername(username)	
} catch(e) {	
    console.log(‘Oh no!’)	
}

然而,在异步环境下,tary-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 原来所在的作用域就很难捕获到回调函数里抛出的异常了。

这就是为什么在 Node.js 里通常使用不同的方式处理错误,而且这使得所有回调函数的参数都需要遵循 (err, ...) 这种形式,其中第一个参数是错误发生时的 error 对象。

错误7:认为 Number 是一种整型数据格式

在 JavaScript 里数字都是浮点型,没有整型的数据格式。你可能认为这不是什么问题,因为数字大到溢出浮点型限制的情况很少出现。可实际上,当这种情况发生时就会出错。因为浮点数在表达一个整型数时只能表示到一个最大上限值,在计算中超过这个最大值时就会出问题。也许看起来有些奇怪,但在 Node.js 中下面代码的值是 true:

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

很不幸的是,JavaScript 里有关数字的怪癖可还不止这些。尽管数字是浮点型的,但如下这种整数运算能正常工作:

5 % 2 === 1 // true	
5 &gt;&gt; 1 === 2 // true

然而和算术运算不同的是,位运算和移位运算只在小于 32 位最大值的数字上正常工作。例如,让“Math.pow(2, 53)” 位移 1 位总是得到 0,让其与 1 做位运算也总是得到 0:

Math.pow(2, 53) / 2 === Math.pow(2, 52) // true	
Math.pow(2, 53) &gt;&gt; 1 === 0 // true	
Math.pow(2, 53) | 1 === 0 // true

你可能极少会去处理如此大的数字,但如果你需要的话,有很多实现了大型精密数字运算的大整数库可以帮到你,比如 node-bigint。

错误8:忽略了流式 API 的优势

现在我们想创建一个简单的类代理 web 服务器,它能通过拉取其他 web 服务器的内容来响应和发起请求。作为例子,我们创建一个小型 web 服务器为 Gravatar 的图像服务。

var http = require(&#39;http&#39;)	
var crypto = require(&#39;crypto&#39;)	
http.createServer()	
.on(&#39;request&#39;, function(req, res) {	
    var email = req.url.substr(req.url.lastIndexOf(&#39;/&#39;)+1)	
    if(!email) {	
        res.writeHead(404)	
        return res.end()	
    }	
    var buf = new Buffer(1024*1024)	
    http.get(&#39;http://www.gravatar.com/avatar/&#39;+crypto.createHash(&#39;md5&#39;).update(email).digest(&#39;hex&#39;), function(resp) {	
        var size = 0	
        resp.on(&#39;data&#39;, function(chunk) {	
            chunk.copy(buf, size)	
            size += chunk.length	
        })	
        .on(&#39;end&#39;, function() {	
            res.write(buf.slice(0, size))	
            res.end()	
        })	
    })	
})	
.listen(8080)

在这个例子里,我们从 Gravatar 拉取图片,将它存进一个 Buffer 里,然后响应请求。如果 Gravatar 的图片都不是很大的话,这样做没问题。但想象下如果我们代理的内容大小有上千兆的话,更好的处理方式是下面这样:

http.createServer()	
.on(&#39;request&#39;, function(req, res) {	
    var email = req.url.substr(req.url.lastIndexOf(&#39;/&#39;)+1)	
    if(!email) {	
        res.writeHead(404)	
        return res.end()	
    }	
    http.get(&#39;http://www.gravatar.com/avatar/&#39;+crypto.createHash(&#39;md5&#39;).update(email).digest(&#39;hex&#39;), function(resp) {	
        resp.pipe(res)	
    })	
})	
.listen(8080)

这里我们只是拉取图片然后简单地以管道方式响应给客户端,而不需要在响应它之前读取完整的数据存入缓存。

错误9:出于 Debug 的目的使用 Console.log

在 Node.js 里,“console.log” 允许你打印任何东西到控制台上。比如传一个对象给它,它会以 JavaScript 对象的字符形式打印出来。它能接收任意多个的参数并将它们以空格作为分隔符打印出来。有很多的理由可以解释为什么开发者喜欢使用它来 debug 他的代码,然而我强烈建议你不要在实时代码里使用“console.log”。你应该要避免在所有代码里使用“console.log” 去 debug,而且应该在不需要它们的时候把它们注释掉。你可以使用一种专门做这种事的库代替,比如 debug。

这些库提供了便利的方式让你在启动程序的时候开启或关闭具体的 debug 模式,例如,使用 debug 的话,你能够阻止任何 debug 方法输出信息到终端上,只要不设置 DEBUG 环境变量即可。使用它十分简单:

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

开启 debug 模式只需简单地运行下面的代码把环境变量 DEBUG 设置到“app” 或“*” 上:

DEBUG=app node app.js

错误10:不使用监控程序

不管你的 Node.js 代码是跑在生产环境或是你的本地开发环境,一个能协调你程序的监控程序是十分值得拥有的。一条经常被开发者提及的,针对现代程序设计和开发的建议是你的代码应该有 <span class="pln">fail</span><span class="pun">-</span><span class="pln">fast</span> 机制。如果发生了一个意料之外的错误,不要尝试去处理它,而应该让你的程序崩溃然后让监控程序在几秒之内重启它。监控程序的好处不只是重启崩溃的程序,这些工具还能让你在程序文件发生改变的时候重启它,就像崩溃重启那样。这让开发 Node.js 程序变成了一个更加轻松愉快的体验。

Node.js 有太多的监控程序可以使用了,例如:

<span class="pln">pm2</span>

<span class="pln">forever</span>

<span class="pln">nodemon</span>

<span class="pln">supervisor</span>

所有这些工具都有它的优缺点。一些擅长于在一台机器上处理多个应用程序,而另一些擅长于日志管理。不管怎样,如果你想开始写一个程序,这些都是不错的选择。

总结

你可以看到,这其中的一些错误能给你的程序造成破坏性的影响,在你尝试使用 Node.js 实现一些很简单的功能时一些错误也可能会导致你受挫。即使 Node.js 已经使得新手上手十分简单,但它依然有些地方容易让人混乱。从其他语言过来的开发者可能已知道了这其中某些错误,但在 Node.js 新手里这些错误都是很常见的。幸运的是,它们都可以很容易地避免。我希望这个简短指南能帮助新手更好地编写 Node.js 代码,而且能够给我们大家开发出健壮高效的软件。

更多node相关知识,请访问:nodejs 教程!!

위 내용은 nodejs에서 흔히 발생하는 상위 10가지 오류는 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.