функция ожидания внутри обещания

#javascript #node.js #cheerio

#javascript #node.js #приветствую

Вопрос:

Я хочу создать новое обещание внутри асинхронной функции. Внутри этого обещания я хотел бы вызвать другую асинхронную функцию, которая вызовет некоторые асинхронные элементы (очистит HTML от веб-страницы).

Вот мой код :

  async extractProductUrlFromHTML(html) {
    const products = html(`h2.product-name`).toArray();
    let extracted = [];
    let promises = [];
    for (let index = 0; index < products.length; index  ) {
        const element = products[index];
        let productUrl = element.children[0].attribs.href;
        const productProm = new Promise(async (resolve, reject) => {
            try {               
                console.log("process");
                const product = await this.getProductInfos(productUrl);
                console.log("resolving")
                resolve(product);
            } catch (error) {
                reject(error)
            }

        })
        promises.push(productProm);

    }
    Promise.all(promises).then((prods) => {
        console.log("promises ok", prods)
        extracted.push(prods);
        return (extracted);
    })
}
  

И вот как вызывается функция :

 async scrapeAllProducts({ request, response }) {
    let extractedProducts = []

    //get the html page
    const html = await AxiosService.getHTML('http://www.ubagcollection.com/fr/ubag.html?limit=36')
    const $ = cheerio.load(html);

    let pagesToScrape = await this.extractPagination($);
    pagesToScrape.push(`http://www.ubagcollection.com/fr/ubag.html?limit=36`)

    for (let index = 0; index < pagesToScraep.length; index  ) {
        const url = pagesToScrape[index];
        let html = await AxiosService.getHTML(url);
        const $ = cheerio.load(html);
        console.log("start extraction")
        const products = await this.extractProductUrlFromHTML($);
        console.log('product extracted from page :'   index)
        extractedProducts.push(products);
    }
    console.log("all is extracted", extractedProducts)
    ... bla bla bla
  

Моя проблема в том, что на данный момент все мои обещания созданы, я вижу «продукт, извлеченный со страницы: …», задолго до того, как программа закончила работать…

вот последовательность, которую я вижу, когда запускаю свой скрипт :

начать извлечение процесс процесс процесс процесс процесс процесс процесс процесс процесс процесс процесс процесс процесс процесс процесс процесс процесс процесс процесс процесс процесс процесс процесс процесс процесс процесс процесс процесс продукт, извлеченный со страницы: 1

каждый раз, когда создается обещание, я регистрирую ‘process’, и я должен увидеть журнал «resolving», когда моя асинхронная функция завершит свою работу, чего здесь нет.

Комментарии:

1. Можете ли вы добавить console.error(error); before reject(error) в extractProductUrlFromHTML , чтобы проверить, не возникает ли какая-либо ошибка? Каков результирующий результат при выполнении этого?

Ответ №1:

Я думаю, вы можете немного почистить свои обещания. Вам не нужно заключать getProductInfos в обещание. Вы await используете ее, поэтому я предполагаю, что она возвращает обещание. Нет необходимости ожидать обещание, просто чтобы обернуть его в другое обещание и разрешить его.

Может быть, что-то более похожее:

 async extractProductUrlFromHTML(html) {
  try {
    const products = html(`h2.product-name`).toArray() || [];
    let extracted = [];
    let promises = products.map(element => {
      let productUrl = element.children[0].attribs.href;
      return this.getProductInfos(productUrl); // assuming getProductInfos returns a promise
    });

    const results = await Promise.all(promises); // need to await the Promise.all
    extracted.push(results); // Do you really want to push the array of results into another array? You'll have [[productInfo1, productInfo2, ...]]
    return extracted;
  } catch (e) {
    console.error(e);
  }
}
  

Ответ №2:

Хорошо, я исправил свою проблему. Я отбрасываю пример кода, возможно, это может быть кому-то полезно.

     let extracted = [];
    let promises = [];
    for (let index = 0; index < products.length; index  ) {
        const element = products[index];
        let productUrl = element.children[0].attribs.href;
        const productProm = new Promise(async (resolve, reject) => {
            try {
                await utils.timeout(utils.getRandomInt(1000,4000))
                const product = await this.getProductInfos(productUrl);
                resolve(product);
            } catch (error) {
                reject(error)
            }

        })
        promises.push(productProm);

    }
    let prods = await Promise.all(promises)
    extracted.push(prods);
  

Комментарии:

1. Вы, вероятно, не хотите делать это таким образом. Вам не нужно заключать эти операции в обещание. Вы могли бы просто извлечь эту логику из обещания в другую асинхронную функцию. Кроме того, если вы отклоните это обещание, в той части, где вы await Promise.all(promises) должны его перехватить, ничего не будет, поэтому у вас будет неперехваченное исключение. Кроме того, в соответствии с тем, как вы написали здесь код, он будет обрабатывать продукты последовательно, а не одновременно, поэтому это займет гораздо больше времени. Вероятно, было бы лучше сопоставить их, преобразовав массив продуктов в массив обещаний, выполняемых одновременно.