Node.JS
十个Node.js开发易犯错误之三:多次执行一个回调函数
有多少次你保存好源码后,然后重新启动Node应用后,你看到了快速的崩溃?大部分情况是由于两次执行回调函数,意思是,在第一次使用以后忘记返回它了。
让我们创建一个案例来重现这个情况,我们曾经一个简单的代理服务器,带有基本验证功能,这需要安装request依赖,运行它然后打开:http://localhost:1337/?url=http://www.google.com/。源码如下:
var request = require('request'); var http = require('http'); var url = require('url'); var PORT = process.env.PORT || 1337;
var expression = /[-a-zA-Z0-9@:%_\+.~#?&//=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9@:%_\+.~#?&//=]*)?/gi; var isUrl = new RegExp(expression);
var respond = function(err, params) { var res = params.res; var body = params.body; var proxyUrl = params.proxyUrl;
console.log('%s - %s', new Date(), proxyUrl);
res.setHeader('Content-type', 'text/html; charset=utf-8');
if (err) { console.error(err);
res.end('An error occured. Please make sure the domain exists.'); } else { res.end(body); } };
http.createServer(function(req, res) { var queryParams = url.parse(req.url, true).query; var proxyUrl = queryParams.url;
if (!proxyUrl || (!isUrl.test(proxyUrl))) { res.writeHead(200, { 'Content-Type': 'text/html' }); res.write("Please provide a correct URL param. For ex: "); res.end("<a href='http://localhost:1337/?url=http://www.google.com/'>http://localhost:1337/?url=http://www.google.com/</a>"); } else { console.log('trying to proxy to %s', proxyUrl);
//注意下面这段代码的问题 request(proxyUrl, function(err, r, body) { if (err) { // auch, forgot to return respond(err, { res: res, proxyUrl: proxyUrl });
console.log(0); }
respond(null, { res: res, body: body, proxyUrl: proxyUrl }); }); } }).listen(PORT); console.log('application started on port %s', PORT); |
在上面代码中我们标注了需要注意的代码部分,在这回调函数中,我们处理了错误情况(if err语句里),但是在调用respond
函数以后忘记停止了执行流程,这样就没有任何网站响应,respond
函数被调用两次,我们在控制台得到下面的信息:
Error: Can't set headers after they are sent
at ServerResponse.OutgoingMessage.setHeader (http.js:691:11)
at respond (/Users/alexandruvladutu/www/airpair-2/3-multi-callback/proxy-server.js:18:7)
This can be avoided either by using the `return` statement or by wrapping the 'success' callback in the `else` statement:
解决方式:
request(.., function(..params) {
if (err) {
return respond(err, ..);
}
respond(..);
});
// OR:
request(.., function(..params) {
if (err) {
respond(err, ..);
} else {
respond(..);
} });
本节源码下载:Github