Search code examples
javascriptecmascript-6promisees6-promise

Handle all promise rejections in nested promises?


I'm using Promise.race so I can time out the fetch call. During a test to an API endpoint that doesn't exist, I'm getting an "Unhandled promise rejection" error. The code is supposed to console.log once upon any failure. Where am I not handling the promise rejection? FYI the main function is function Retrieve

window.path = "http://localhost:3000/records";

function isPrimary(color)  {

    let colorIsPrimary = false;
    (color.startsWith("red") || color.startsWith("blue") || color.startsWith("yellow")) ? colorIsPrimary = true : null;
    return colorIsPrimary;

}

function transformData(records,page) {

    const transformedData = {
        "ids" : [],
        "open": [],
        "closedPrimaryCount": 0,
        "previousPage": null,
        "nextPage": null
    }

    let closedPrimaryCount = 0;

    records.forEach((record, index) => {

        if (index < 10) {

            transformedData.ids.push(record.id);

            record["isPrimary"] = false;
            isPrimary(record.color) ? record.isPrimary = true : null;
        
            record.disposition == "open" ? transformedData.open.push(record) : null;

            if (record.disposition == "closed") {
                isPrimary(record.color) ? closedPrimaryCount++ : null;   
            }

        }

    })

    transformedData.closedPrimaryCount = closedPrimaryCount;

    let previousPage = null;
    page > 1 ? previousPage = page - 1 : null;
    transformedData.previousPage = previousPage;

    let nextPage = null;
    records.length > 10 ? nextPage = page + 1 : null;
    transformedData.nextPage = nextPage;

    return transformedData;
   
}

function promiseTimeout(promise, ms) {

    let timeout = new Promise((resolve, reject) => {
        let timeoutID = setTimeout(() => {
            clearTimeout(timeoutID);
            reject("fetch failed - timeout");
        }, ms)
    })

    return Promise.race([promise, timeout]);

}

function doFetch(url) {

    return new Promise((resolve, reject) => {

        fetch(url)

        .then((response) => {

            if (!response.ok) {
      
                reject(new Error("fetch failed - non 200"));
      
            }

            response.json()
        
            .then((records) => {

                resolve(records);

            })

            .catch((error) => {

                reject(new Error("fetch failed - error from response.json"));
            
            })    

        })

        .catch((error) => {

            reject(new Error("fetch failed - error from fetch"));
        
        })

    }) 

}

function retrieve({page = 1, colors = ["red", "brown", "blue", "yellow", "green"], thetest = false, windowPath = window.path} = {}) { 

    return new Promise((resolve,reject)=>{

        !thetest ? windowPath = "http://localhost:3000/records" : null;

        // limit set to 11 so we can determine nextPage
        const limit = "11";

        const offset = "" + ((page * 10) - 10);

        let colorArgs = "";
        colors.forEach((color, index) => {
            colorArgs = colorArgs + "&color[]=" + color; 
        });

        const requestQuery = `limit=${limit}&offset=${offset}${colorArgs}`;
        
        const requestURL = new URI(windowPath);
        requestURL.query(requestQuery);

        const promiseRace = promiseTimeout(doFetch(requestURL.toString()), 4000);

        promiseRace.then((records) => {

            const transformedData = transformData(records, page);

            resolve(transformedData);

        })

        promiseRace.catch((error) => {

            console.log(error);

        })        

    });

};

export default retrieve;

Solution

  • After ggorlen's excellent advice, I refactored to this much cleaner (and test-passing) code:

    async function getTransformedData(url,page) {
    
        try {
    
            const response = await fetch(url);
    
            if (response.ok) {
    
                const records = await response.json();
    
                const transformedData = transformData(records,page);
    
                return(transformedData);
    
            } else {
    
                throw new Error("failed");    
    
            }
    
        }
    
        catch(error) {
    
            console.log(error);
    
        }
        
        // getTransformedData
    }
    
    
    function retrieve({page = 1, colors = ["red", "brown", "blue", "yellow", "green"]} = {}) { 
    
        // limit set to 11 so we can determine nextPage
        const limit = "11";
    
        const offset = "" + ((page * 10) - 10);
    
        let colorArgs = "";
        colors.forEach((color, index) => {
            colorArgs = colorArgs + "&color[]=" + color; 
        });
    
        const requestQuery = `limit=${limit}&offset=${offset}${colorArgs}`;
        
        const requestURL = new URI(window.path);
        requestURL.query(requestQuery);
    
        return getTransformedData(requestURL.toString(),page);
    
    };