如何将多个响应合并到一个json中?谢谢!
当我运行代码时,我收到错误:
错误[ERR_HTTP_HEADERS_SENT]:无法在将标头发送到客户端后设置标头
...
var trackArrayReg = [
/^[A-Z]{2}\d{14}NPI$|^460\d{9}$|^959\d{9}$/i,
/^LP0\d{13}$|^[A-Z]{2}\d{14}NPI$/i
];
router.get('/:trackId', function(req, res) {
var trackId = req.params.trackId;
trackArrayReg.forEach(function(item, index, urlsArray) {
if (trackArrayReg[index].exec(trackId)) {
track = index;
request({
method: config[track].method,
url: config[track].url + trackId,
timeout: config[track].timeout,
maxAttempts: 3,
retryDelay: 500
}, function(err, response, body, callback) {
if (err) return console.error(err);
$ = cheerio.load(body);
stat = [];
$(config[track].response.rowSelector).map(function(i, links) {
var date = $(links).find(config[track].response.columnSelector).eq(config[track].response.dateColumnIndex).text(),
status = $(links).find(config[track].response.columnSelector).eq(config[track].response.stateColumnIndex).text(),
location = $(links).find(config[track].response.columnSelector).eq(config[track].response.locationColumnIndex).text();
stat.push({
location: location,
date: date,
status: status,
carrier: track
});
});
var states = JSON.parse(JSON.stringify(stat));
res.send({states});
});}});});
...
答案 0 :(得分:0)
看起来传入请求会触发多个外部请求,这些请求会针对某些统计信息进行解析,这些统计信息最终会被合并,然后包含在对原始传入请求的响应中。如果这是准确的,那么这是处理多个异步请求的问题。
如何解决这个问题需要我们看一下目前正在发生的事情。由于外部请求都是由forEach
循环并行有效触发的异步调用,因此首先响应的请求将是触发对原始传入请求的响应的请求。遗憾的是,剩余的外部请求一旦收到响应,也会尝试响应原始的传入请求。因此Express的错误是试图不止一次回应。
有效地,代码目前正在执行此操作(假设为x > y
):
incoming req --> external req #1 (t0) --> response (t0+x) --> res.send ❌
--> external req #2 (t0) --> response (t0+y) --> res.send ✅
注意:外部请求处于竞争状态,因此,首先收到响应的响应将首先响应原始传入请求。
要解决此问题,需要有一种方法来管理异步外部请求,然后组合它们的输出。
通过使用promises可以处理解决方案的第一部分,管理异步请求。由于request
已经在代码中使用,因此使用promise版本request-promise
是一个简单的转换。 注意:我们还可以使用transform
中的request-promise
函数来简化通过cheerio
进行正文解析。
下一部分是结合外部请求的解析响应。
目前,上述代码并行触发了大量请求。假设这不是问题,我们可以将Promise.all
与“地图”一起使用,而不是forEach
,等待所有请求回复。
注意:鉴于每个请求似乎都会创建一个值数组,整体结果将是这些值数组的数组。
因此,在我们的流程图中,我们将:
inc req --> p.all(map(ext req #1 (t0) --> res (t0+2n) -->)) --> res.send ✅
(ext req #2 (t0) --> res (t0+n) -->)
将所有内容放在一起(使用其他一些语法编辑): 注意:这是未经测试的代码。
const requestPromise = require('request-promise-native');
const trackArrayReg = [
/^[A-Z]{2}\d{14}NPI$|^460\d{9}$|^959\d{9}$/i,
/^LP0\d{13}$|^[A-Z]{2}\d{14}NPI$/i
];
router.get('/:trackId', ({params: {trackId}}, res) => {
Promise.all(trackArrayReg.map((regexp, track) => {
if (regexp.test(trackId)) {
return requestPromise({
method: config[track].method,
url: config[track].url + trackId,
timeout: config[track].timeout,
maxAttempts: 3,
retryDelay: 500,
transform(body) {
return cheerio.load(body);
}
}).then(($) => {
const rows = $(config[track].response.rowSelector);
return rows.map((links) => {
const columns = $(links).find(config[track].response.columnSelector);
const date = columns.eq(config[track].response.dateColumnIndex).text();
const status = columns.eq(config[track].response.stateColumnIndex).text();
const location = columns.eq(config[track].response.locationColumnIndex).text();
return {
location,
date,
status,
carrier: track
};
});
}).catch((err) => {
console.error(err)
});
}
})).then((stats) => {
// Combine or modify the stats array as desired
res.json(stats);
});
});
对于重构的进一步练习,您还可以使用async / await的语法糖来帮助管理promises。我会把它留作好奇的练习。
希望这有帮助!