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

下页

Node.js最佳实践