我正在尝试制作一个小型应用程序,用于为给定用户的照片拇指抓取 500px.com(照片网站)的 rss 提要。我对这一切还比较陌生。
我在本地测试时看到了一个我不理解的行为。页面第一次加载正常,但重新加载时,我收到“发送后无法发送标题”。从这里的其他答案来看,这似乎通常是由双回调引起的。我尝试在不同的地方放置一些 if (err) res.send();
语句,但它似乎并没有解决问题。我也尝试过重新配置代码的结构,但似乎也没有用。
比我更有经验的人能看出这里发生了什么吗?
var express = require('express');
var router = express.Router();
var request = require('request');
var parseString = require('xml2js').parseString;
const cheerio = require('cheerio');
var EventEmitter = require('events').EventEmitter;
var body = new EventEmitter();
/* GET home page. */
router.get('/', function(req, res, next) {
request("https://500px.com/janedoe/rss", function(error, response, data) {
body.data = data;
body.emit('update');
});
body.on('update', function() {
parseString(body.data, function (err, result) {
// the stuff below likely isn't relevant to the problem, just some testing
var photoLink = result.rss.channel[0].item[0].description[0];
const $ = cheerio.load(photoLink);
const links = $('img');
const linkString = links.attr('src').toString();
// end of area probably not relevant
res.render('index', { title: 'Express', linkString});
});
});
});
module.exports = router;
最佳答案
这里有多个问题。可能导致您询问的特定错误的原因是,每次您的路线被击中时,您都会向 EventEmitter 对象添加另一个监听器,因此在第二次击中路线后,您有两个监听器,因此当您发出时,您有两个被调用的监听器,您尝试发送两个响应,其中一个发送给已经发送响应的旧响应对象。
这里的核心问题是您试图在请求中使用模块级变量。这会导致不同请求之间的交叉耦合(它们在处理请求时都试图潜在地使用相同的发射器对象)。一旦有多个用户使用您的系统,这只是一种等待发生的竞争条件。
我完全不知道您为什么要在这里使用 EventEmitter
对象,因为您似乎不需要一个。当请求收集了所有数据时,您可以只处理最终结果,根本不使用 EventEmitter。这将解决竞争条件和多个处理程序。
这是一种修复方法:
router.get('/', function(req, res, next) {
request("https://500px.com/janedoe/rss", function(error, response, data) {
if (error) {
next(error);
return;
}
parseString(data, function(err, result) {
if (err) {
next(err);
return;
}
// the stuff below likely isn't relevant to the problem, just some testing
var photoLink = result.rss.channel[0].item[0].description[0];
const $ = cheerio.load(photoLink);
const links = $('img');
const linkString = links.attr('src').toString();
// end of area probably not relevant
res.render('index', {
title: 'Express',
linkString
});
});
});
});
变更摘要:
- 将
parseString()
操作移到request()
回调中。 - 完全摆脱共享 EventEmitter 的使用。
- 为两个异步操作添加错误处理。
如果出于某种原因,您需要或想要在代码中使用 EventEmitter
(尽管当前代码没有说明您需要使用的原因),您必须创建一个新的一个用于您正在处理的每个请求,并且仅将其存储在该特定请求的范围内(而不是在模块级变量中)。这既可以防止飞行中的不同请求之间的串扰,也可以防止为同一事件添加多个监听器到您的 EventEmitter
对象,因为每个请求都有一个单独的 EventEmitter
,而您只向该 EventEmitter
对象添加一个监听器。这是它的工作原理:
/* GET home page. */
router.get('/', function(req, res, next) {
// make EventEmitter object for this particular request
let body = new EventEmitter();
request("https://500px.com/janedoe/rss", function(error, response, data) {
if (error) {
next(error);
return;
}
body.data = data;
body.emit('update');
});
body.on('update', function() {
parseString(body.data, function(err, result) {
if (err) {
next(err);
return;
}
// the stuff below likely isn't relevant to the problem, just some testing
var photoLink = result.rss.channel[0].item[0].description[0];
const $ = cheerio.load(photoLink);
const links = $('img');
const linkString = links.attr('src').toString();
// end of area probably not relevant
res.render('index', {
title: 'Express',
linkString
});
});
});
});
关于javascript - Node.js 发送后无法发送 header ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45639646/