Как получить данные после цикла, используя обещание

Привет, кто-нибудь может мне помочь с моей асинхронной проблемой. я делаю веб-скребок и после того, как я скребу сеть. мне нужно поместить данные в мою базу данных (mongodb) после их ввода. мне нужно отправить их в интерфейс. но так как у меня есть цикл элементов, я не могу поставить res.json() внутри itll выдал ошибку (отправить можно только один раз после res,json())

я застрял здесь. Я раньше использовал обещания, но это сбивает с толку.

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... внутри цикла, потому что мне нужны все данные, чтобы иметь идентификатор. благодарю вас.

3 ответа

Вместо того, чтобы пытаться накапливать результаты напрямую, вы можете отобразить элементы, содержащиеся в $('h2.story-heading, p.summary') к массиву обещаний, а затем объединить с 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 лучше, чем документация Cheerio (но будьте осторожны, потому что Cheerio - компактная версия jQuery).

  2. Ни в коем случае вам не нужно new Promise(), Все нужные вам обещания возвращаются articles.create(),

Примерно так может работать (код не проверен)

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

Используйте функцию.map, чтобы вернуть все обещания в 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);
        });
    })
Другие вопросы по тегам