如何将多个回复合并为一个 json

How to merge multiple responses into one json

如何将多个回复合并为一个json?谢谢!

当我 运行 代码时出现错误:

错误[ERR_HTTP_HEADERS_SENT]:发送给客户端后无法设置headers

.....

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});
          });}});});

.....

看起来传入请求正在触发多个外部请求,这些请求被解析为一些最终要组合的统计信息,然后包含在对原始传入请求的响应中。如果这是准确的,那么这是处理多个异步请求的问题。

如何解决这个问题需要我们看看目前正在发生的事情。由于外部请求都是由 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注意:我们还可以利用 request-promise 中的 transform 函数来简化通过 cheerio.

的正文解析

下一部分是合并来自外部请求的解析响应。

目前,上述代码正在并行触发一堆请求。假设这不是问题,我们可以使用 Promise.all 和 'map',而不是 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 的语法糖来帮助管理承诺。我会把它作为好奇的练习。

希望对您有所帮助!