Javascript Node.js可以';发送邮件头后,不要发送邮件头
我正在尝试制作一个小应用程序,它可以从500px.com(一个照片网站)的rss提要中获取给定用户的照片拇指。我对这一切还比较陌生 在本地测试时,我看到一种我不理解的行为。页面在第一次加载时可以正常加载,但在重新加载时,我会得到“发送后无法发送标题”。从这里的其他答案来看,这似乎通常是由双重回调引起的。我试着把一些Javascript Node.js可以';发送邮件头后,不要发送邮件头,javascript,node.js,express,Javascript,Node.js,Express,我正在尝试制作一个小应用程序,它可以从500px.com(一个照片网站)的rss提要中获取给定用户的照片拇指。我对这一切还比较陌生 在本地测试时,我看到一种我不理解的行为。页面在第一次加载时可以正常加载,但在重新加载时,我会得到“发送后无法发送标题”。从这里的其他答案来看,这似乎通常是由双重回调引起的。我试着把一些if(err)res.send()语句在不同的地方,但它似乎没有解决问题。我还尝试重新配置代码的结构,这似乎也不起作用 有没有比我更有经验的人知道这里发生了什么 var express
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
});
});
});
});
您似乎可以将body.on('update…
)中的所有内容移动到请求的回调中。如果请求失败,您还应该进行错误处理。我不知道应用程序的其余部分是什么样子,但这里不需要EventSemiter
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) {
parseString(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
,以避免出现这种情况
简单地划掉这条线:
var body = new EventEmitter();
并将其粘贴到路线中:
router.get('/', function(req, res, next) {
var body = new EventEmitter(); // Paste it here
request("https://500px.com/janedoe/rss", function(error, response, data) {
body.data = data;
body.emit('update');
});
// The rest of your code here ...
});
在哪里导入?@jfriend00很好地解释了为什么在他的回答中,我想我已经弄明白了为什么这是错误的方法。谢谢!非常感谢你的解释。所以基本上每次客户端加载页面时,它都会提示一个新的EventEmitter实例——因此,当更新启动时,它会尝试呈现twi对同一个客户端执行一次或多次,但它不能执行?@testingtesting-不完全正确。您的代码只有一个EventEmitter实例,但每次客户端加载页面时,您都会在该实例上添加另一个侦听器。然后当您.emit()
对于它,所有侦听器都会触发,导致它发送多个响应,从而触发您看到的日志消息。此外,您还存在一个漏洞,因为您与所有请求处理程序共享一个EventEmitter
,因此每当您的服务器为多个用户提供服务时,您都容易受到以下两者之间的串扰/竞争条件的影响n个不同的用户。@testingtesting-我添加了一个示例,通过为每个请求创建一个新的EventEmitter
对象,您可以在代码中安全地使用EventEmitter
。明白了,很有意思。所以实际上问题是,如果我理解的话,我试图在同一个EventEmitter实例上执行多次更新正确地