Simon Breton Simon Breton - 2 months ago 9
Javascript Question

Returning error as an array object value with promise

I'm using a scraping function to get some data from a bunch of

urls
listed inside an array. Here is the following function :

function getNbShares(urls) {
return Promise.map(urls, request).map((htmlOnePage, index) => {
const $ = cheerio.load(htmlOnePage),
share = $('.nb-shares').html();
return {
url: urls[index],
value: share
};
}).catch(function (urls, err) {
return {
url: urls[index],
value: err
};
});
}


It's working fine, however the error handling isn't. What I would like is that when I have an error (either because the page doesn't load or if the DOM selector is wrong) the map function/request keep doing is job and it returns me the error (or null) as a value attached to the url in the final array object.

Answer

I think you just want to do that handling a bit earlier, within the mapping function; and I think you can avoid having two separate mapping operations; see comments:

function getNbShares(urls) {
    return Promise.map(
        urls,
        url => request(url)
                .then(htmlOnePage => {                   // Success, so we parse
                    const $ = cheerio.load(htmlOnePage), // the result and return
                        value = $('.nb-shares').html();  // it as an object with
                    return { url, value };               // `url` and `value` props
                })
                .catch(error => ({url, error}))          // Error, so we return an
                                                         // object with `url` and
                                                         // `error` props
        }
    );
}

(I've assumed you're using ES2015+, as you were using arrow functions.)

I might opt to factor part of that out:

function getNbSharesFromHTML(html) {
    const $ = cheerio.load(html);
    return $('.nb-shares').html();
}
function getNbShares(urls) {
    return Promise.map(
        urls,
        url => request(url)
                .then(htmlOnePage => ({url, value: getNbSharesFromHTML(htmlOnePage)))
                .catch(error => ({url, error}))
        }
    );
}

Possibly even smaller pieces:

function getNbSharesFromHTML(html) {
    const $ = cheerio.load(html);
    return $('.nb-shares').html();
}
function getNbSharesFromURL(url) {
    return request(url)
            .then(htmlOnePage => ({url, value: getNbSharesFromHTML(htmlOnePage)))
            .catch(error => ({url, error}));
}
function getNbShares(urls) {
    return Promise.map(urls, getNbSharesFromURL);
}