Nested Async await inside timer - not returning the desired value

Viewed 329

I have to test response of endpoints using Mocha and chai tests. Below is the code for the same :

async function getData (userId) {
        let response;
        let interval = setInterval(async () => {
            response = await superagent.get("localhost:3000/user/details/").query({'user': userId}).type('application/json');
            if (response.body["status"] == 'DONE') {
                clearInterval(interval);
                response = await superagent.get("localhost:3000/user/details/get").type('application/json');
            }
        }, 10000);

    return response;    

}

Test Code :

it('User Get Data', async function () {
        return getData(userId,).then(function (res) {
            expect(res).to.exist;
            expect(res.status).to.equal(200);
            expect(res.body).to.contain('operation');
            expect(res.body["userDetails"]).to.exist;


        });

I always get the response as null and my test fails . Kindly let me know where am I going wrong with this code.

2 Answers

Edited to get rid of the while loop:

You can rewrite getData with async/await and wrapping your interval in a promise. Once the first response is ok, clear the interval, resolve the promise and execute the second call.

In your unit-test simply await this function and then verify the response details. Note that you might want to increase the default mocha-timeout for the test, as this could potentially take a while. Something like:

async function getData(userId) {

    const firstResponsePromise = new Promise(resolve => {
            const interval = setInterval(async() => {
                    const response = await superagent.get('localhost:3000/user/details/').query({
                            'user': userId
                        }).type('application/json');
                    if (response.body['status'] == 'DONE') {
                        clearInterval(interval);
                        resolve();
                    }

                }, 10000)
        });

    await firstResponsePromise;
    return superagent.get('localhost:3000/user/details/get').type('application/json');

}

// unit test

it('User Get Data', async function () {
    const res = await getData(userId);
    expect(res).to.exist;
    expect(res.status).to.equal(200);
    expect(res.body).to.contain('operation');
    expect(res.body["userDetails"]).to.exist;

});

Don't use setInterval with promises, and never pass an async function as a callback when the returned promise is ignored. In your case, use a loop instead:

async function getData (userId) {
    let response;
    do {
        await delay(10000);
        response = await superagent.get("localhost:3000/user/details/").query({'user': userId}).type('application/json');
    } while(response.body["status"] != 'DONE');

    return superagent.get("localhost:3000/user/details/get").type('application/json');
}

with

function delay(t) {
    return new Promise(resolve => setTimeout(resolve, t));
}
Related