【问题标题】:How to get data after loop using promise如何使用promise在循环后获取数据
【发布时间】:2019-01-26 00:12:44
【问题描述】:

我正在处理一个异步问题。我正在制作一个网络抓取工具,在抓取网络之后,我需要将数据放入我的 MongoDB 数据库中。我需要将它发送到前端,但是由于我有一个循环元素,所以我不能把res.json()放进去,会报错(res.json())之后只能发送一次。

我被困在这里了。我以前使用过 promise,但这很令人困惑。

router.get('/scrape', (req, res) => {
  request('http://www.nytimes.com', function test(error, response, html) {
    const $ = cheerio.load(html);

    // An empty array to save the data that we'll scrape
    const results = [];

    $('h2.story-heading, p.summary').each(function(i, element) {
      const link = $(element)
        .children()
        .attr('href');
      const title = $(element)
        .children()
        .text();
      const summary = $(element)
        .children()
        .text();

      const data = {
        title: title,
        link: link,
        summary: summary,
      };

      articles
        .create(data)
        .then((resp) => results.push(resp))
        // .then((resp) => Promise.resolve(results)) //
        // .then((jsonDta ) => res.json(jsonData)) // error you can only give response once.
        .catch((err) => reject(err));
    });
    console.log(results); // empty array
    res.json(results)// empty 
  });
});

我的计划是:

  • 抓取网站(循环元素)
  • 然后保存到 MongoDB(将数据推送到数组中)
  • 然后在循环之后将其传递给前端。

我需要将查询方法create... 放在循环中,因为我需要每个数据都有一个id。

【问题讨论】:

  • 在这种情况下我应该把我的承诺放在哪里?那是我的问题。

标签: node.js asynchronous promise es6-promise scrape


【解决方案1】:

使用.map函数将所有promise返回给Promise.all,然后返回结果。

      request('http://www.nytimes.com', function test(error, response, html) {
        const $ = cheerio.load(html);

        var summary = $('h2.story-heading, p.summary')
        Promise.all(summary.map((i, element) =>{
            const data = {
              title: $(element).children().text(),
              link: $(element).children().attr('href'),
              summary: $(element).children().text(),
            };

           return articles
            .create(data)

        }).get())
        .then((result)=>{
        console.log(result);
        res.json(result);
        });
    })

【讨论】:

  • 谢谢您,先生。生病试试这个。我尝试了 forEach 但给了我一个错误,因为cheerio 只在每个循环中构建?我想。这也可能给我带来错误。
  • @artoo Cheerio 确实包含.map(),你可以查看:)
  • 嘿伙计。还是不行。我想知道这不再是关于承诺了
  • 您好,先生。是的,我变得不确定,但现在可以了。一些给定的解决方案有效。非常感谢。
  • “集成”并不是真正正确的概念。 jQuery 专为客户端(浏览器)使用而设计。 Cheerio 是专为服务器端 (node.js) 使用而设计的 jQuery 衍生产品。
【解决方案2】:

这样的事情可能会起作用(代码未经测试)

router.get('/scrape', (req, res) => {
  request('http://www.nytimes.com', function test(error, response, html) {
    const $ = cheerio.load(html);

    // An empty array to save the data that we'll scrape
    const results = [];

    $('h2.story-heading, p.summary').each(function(i, element) {
      const link = $(element)
        .children()
        .attr('href');
      const title = $(element)
        .children()
        .text();
      const summary = $(element)
        .children()
        .text();

      const data = {
        title: title,
        link: link,
        summary: summary,
      };

      const articleCreate = articles.create(data); 
      results.push(articleCreate);

    });

    console.log(results); // this is array of promise functions.

    Promise.all(results).then(allResults => {
      res.json(allResults)
    });

    // or you could use array.reduce for sequantial resolve instead of Promise.all
  });
});

【讨论】:

  • 这个也可以,但我不知道我会使用哪一个。我只是要在上面测试两者。谢谢你,先生。
  • 感谢您抽出时间回答这个问题,先生。欣赏它。
  • 是的,但是results 是一个 Promises 数组,而不是结果。
【解决方案3】:

您可以将$('h2.story-heading, p.summary') 中包含的元素映射到promise 数组,然后与Promise.all() 聚合,而不是尝试直接累积结果。您想要的结果将由Promise.all(...).then(...)提供。

router.get('/scrape', (req, res) => {
    request('http://www.nytimes.com', function test(error, response, html) {
        const $ = cheerio.load(html);
        const promises = $('h2.story-heading, p.summary')
        .get() // as in jQuery, .get() unwraps Cheerio and returns Array
        .map(function(element) { // this is Array.prototype.map()
            return articles.create({
                'title': $(element).children().text(),
                'link': $(element).children().attr('href'),
                'summary': $(element).children().text()
            })
            .catch(err => { // catch so any one failure doesn't scupper the whole scrape.
                return {}; // on failure of articles.create(), inject some kind of default object (or string or whatever).
            });
        });
        // At this point, you have an array of promises, which need to be aggregated with Promise.all().
        Promise.all(promises)
        .then(results => { // Promise.all() should accept whatever promises are returned by articles.create().
            console.log(results);
            res.json(results);
        });
    });
});

如果您希望任何单一故障破坏整个抓取,则省略 catch() 并将 catch() 添加到 Promise.all().then() 链。

注意事项:

  1. 对于.get()(和大多数其他方法),jQuery documentationCheerio documentation 更好(但要小心,因为 Cheerio 是 jQuery 的精简版本)。

  2. 您在任何时候都不需要new Promise()。您需要的所有承诺都由articles.create()返回。

【讨论】:

  • 这行得通,先生。我尝试了这种方法,但没有get() 它会返回一堆对象。我正在使用每个。太烦人了。
  • @aRtoo:你能接受这个答案吗?虽然这不是强制性的,但它会奖励发布者的帮助,并且这样做很好。如果您不熟悉如何接受答案,有文档说明如何接受答案。
猜你喜欢
  • 1970-01-01
  • 2018-06-08
  • 1970-01-01
  • 2017-06-30
  • 1970-01-01
  • 2016-07-30
  • 2019-01-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多