Search code examples
javascriptnode.jspromisees6-promise

Retry functionality in promise chain


i have a promise chain

If i receive error in getServiceCost I want to repeat the chain again (retry) for 2 times how can i achieve this when using Promise chain , meaning again execute getUser, getServiceCost

getUser(100)
    .then(getServices)
    .then(getServiceCost)
    .then(console.log);


function getUser(userId) {
    return new Promise((resolve, reject) => {
        console.log('Get the user from the database.');
        setTimeout(() => {
            resolve({
                userId: userId,
                username: 'admin'
            });
        }, 1000);
    })
}

function getServices(user) {
    return new Promise((resolve, reject) => {
        console.log(`Get the services of ${user.username} from the API.`);
        setTimeout(() => {
            resolve(['Email', 'VPN', 'CDN']);
        }, 3 * 1000);
    });
}

function getServiceCost(services) {
    return new Promise((resolve, reject) => {
        console.log(`Calculate the service cost of ${services}.`);
        setTimeout(() => {
            resolve(services.length * 100);
        }, 2 * 1000);
    });
}

If i receive error in getServiceCost I want to repeat the chain again (retry) for 2 times how can i achieve this when using Promise chain , meaning again execute getUser, getServiceCost


Solution

  • I'd use an async function (all modern environments support them, and you can transpile for obsolete environments), which lets you use a simple loop. Perhaps as a utility function you can reuse:

    async function callWithRetry(fn, retries = 3) {
        while (retries-- > 0) {
            try {
                return await fn();
            } catch (error) {
                if (retries === 0) {
                    throw error;
                }
            }
        }
        return new Error(`Out of retries`); // Probably using an `Error` subclass
    }
    

    Using it:

    callWithRetry(() => getUser(100).then(getServices).then(getServiceCost))
    .then(console.log)
    .catch(error => { /*...handle/report error...*/ });
    

    Or

    callWithRetry(async () => {
        const user = await getUser(100);
        const services = await getServices(user);
        return await getServiceCost(services);
    })
    .then(console.log)
    .catch(error => { /*...handle/report error...*/ });