简体   繁体   English

从使用多个异步调用的函数返回

[英]Return from function that uses multiple async calls

So the basic idea was to write a method that will scrap webpage to get JSON data that contains rating of a product. 因此,基本思想是编写一种方法,该方法将废弃网页以获取包含产品等级的JSON数据。 Then call this method multipletimes over few domains (.de, .uk, .fr, .nl etc) to collect all ratings. 然后在几个域(.de,.uk,.fr,.nl等)上多次调用此方法,以收集所有评分。

So I ended up with scrapWebPage method which scraps single page: 因此,我最终得到了scrapWebPage方法,该方法可以scrapWebPage单个页面:

const scrapWebPage = async (countryAppData, productNumber) => {
    const shopUrl = `https://www.shopExample.${countryAppData.countryCode}/?q=${productNumber}`
    const avoidCORSUrl = 'https://allorigins.me/get?url=' + shopUrl + '&callback=?'

    return await axios
        .get(avoidCORSUrl, {xmlMode: false, normalizeWhitespace: true})
        .then(response => {
            const $ = cheerio.load(response.data)
            let scrapedWebPageJson

            contentForParsing = $("script").get().children[0].data                   
            scrapedWebPageJson = JSON.parse(contentForParsing)

            return scrapedWebPageJson
        })
}

scrapWebPage also contains some parsing to return some JSON data I want - it resolves correctly (tested this) and returns Promise. scrapWebPage还包含一些解析,以返回我想要的一些JSON数据-可以正确解析(测试)并返回Promise。

But then I'd like to call this method over multiple domains so I created getProductDataFromManyDomains : 但是然后我想在多个域上调用此方法,所以我创建了getProductDataFromManyDomains

const getProductDataFromManyDomains = (productNum) => {
    let prodData = {
        reviews: []
    }

    const appCountries = [
        {countryCode: 'nl'}, 
        {countryCode: 'pl'},
        {countryCode: 'de'}
    ]

    appCountries.forEach(async countryApp => {
        let countryData = {}

        let parsedWebPage = await scrapWebPage(countryApp, productNum)

        countryData.countryCode  = countryApp.countryCode
        countryData.ratingCount  = parsedWebPage.aggregateRating.ratingCount
        countryData.ratingValue  = parsedWebPage.aggregateRating.ratingValue
        countryData.reviews      = parsedWebPage.reviews   

        prodData.reviews.push(countryData)
    })

    return prodData
}

And now I receive prodData before populating... while I'd like to receive actual data (populated prodData ). 现在我prodData在填充之前接收prodData ,而我想接收实际数据(填充的prodData )。

I'm not sure how I should construct this getProductDataFromManyDomains method to actually return data and not prodData before populating. 我不确定如何构造此getProductDataFromManyDomains方法以在填充之前实际返回数据而不是prodData Is that possible? 那可能吗? Or what is good pattern here to deal with stuff like that? 还是在这里处理这种事情的好模式是什么?

Use a for loop instead of .forEach() . 使用for循环而不是.forEach() The for loop will pause for await, the .forEach() loop will not. for循环将暂停等待, .forEach()循环不会暂停。 This is because the async callback you pass to .forEach() will return a promise, but .forEach() is not designed to do anything with that promise so it does not wait for it to resolve before continuing the loop, but a for loop using await does. 这是因为传递给.forEach()async回调将返回一个promise,但是.forEach()并非设计为对该promise进行任何操作,因此在继续循环之前,它不会等待它解决,而是一个for循环使用await

Then, getProductDataFromManyDomains() will need to be async and will return a promise with your final result. 然后, getProductDataFromManyDomains()将需要async ,并将返回最终结果的Promise。

async function getProductDataFromManyDomains(productNum) {
    let prodData = {
        reviews: []
    }

    const appCountries = [
        {countryCode: 'nl'}, 
        {countryCode: 'pl'},
        {countryCode: 'de'}
    ]

    for (let countryApp of appCountries) {
        let countryData = {}

        let parsedWebPage = await scrapWebPage(countryApp, productNum)

        countryData.countryCode  = countryApp.countryCode
        countryData.ratingCount  = parsedWebPage.aggregateRating.ratingCount
        countryData.ratingValue  = parsedWebPage.aggregateRating.ratingValue
        countryData.reviews      = parsedWebPage.reviews   

        prodData.reviews.push(countryData)
    })

    // this will be the resolved value of the promise that
    //   getProductDataFromManyDomains() returns
    return prodData;
}

// usage
getProductDataFromManyDomains(productNum).then(result => {
    console.log(result);
});

You could also run your multiple requests in parallel rather than one at a time, but since you originally attempted to make your code do them one at a time, I showed you how to do that. 您也可以并行运行多个请求,而不是一次运行,但是由于您最初试图让您的代码一次执行一次,因此,我向您展示了如何执行。

If you wanted to do them in parallel, you would just accumulate the promises in an array and use Promise.all() to know when they are all done and you would not await the request. 如果您想并行执行它们,则只需将promise堆积在数组中,然后使用Promise.all()知道何时完成它们,而您不会await请求。

Here's a version of the code that runs the requests in parallel, using .map() and Promise.all() : 这是使用.map()Promise.all()并行运行请求的代码版本:

function getProductDataFromManyDomains(productNum) {
    let prodData = {
        reviews: []
    }

    const appCountries = [
        {countryCode: 'nl'}, 
        {countryCode: 'pl'},
        {countryCode: 'de'}
    ]

    return Promise.all(appCounteries.map(countryApp => {

        return scrapWebPage(countryApp, productNum).then(parsedWebPage => {
            let countryData = {}
            countryData.countryCode  = countryApp.countryCode
            countryData.ratingCount  = parsedWebPage.aggregateRating.ratingCount
            countryData.ratingValue  = parsedWebPage.aggregateRating.ratingValue
            countryData.reviews      = parsedWebPage.reviews 
            return countryData;         
        });
    })).then(results => {
        // put results into prodData and make that the resolved value
        prodData.reviews = results;
        return prodData;
    });
}

getProductDataFromManyDomains(productNum).then(result => {
    console.log(result);
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM