N N
N N

Reputation: 1638

How to handle promise recursions

I have a automated script written in javascript, the task it should accomplish is to click on Get Tasks button, which triggers ajax call and results of the call are displayed in table, the next step is to check the table, if there is a task, script accepts it by clicking on Accept button, else, script clicks on Get Tasks button. Task seems simple so I wrote following script:

function clickGetTasksButton() {
    return new Promise(resolve => {
        document.getElementsByClassName("get-tasks-btn").click();
        resolve();
    });
}
function waitForSpinner() {
    return new Promise((resolve) => {
        let spinInterval = setInterval(() => {
            let spinner = document.querySelector('.spinner');
            if (!spinner) {
                console.log("Spinner is gone... ");
                clearInterval(spinInterval);
                resolve(document.getElementsByClassName("task-card").length > 0)
            }
        }, 50)
    });
}
function driver() {
    clickGetTasksButton().then(() => {
        waitForSpinner().then((result) => {
            //Script should either accept task or repeat the whole process
        });
    });
}

I am struggling with adding following logic: if there is Accept button, then click on it, else click on Get Tasks button and repeat. I thought of using recursions, but I am afraid that at some point it will throw stack overflow exception.

Upvotes: 0

Views: 32

Answers (1)

jfriend00
jfriend00

Reputation: 708206

You can just freely recurse from a .then() handler. Because the stack has already completely unwound before a .then() handler is called, there is no stack buildup when you just call driver() again or whatever other top level function you want to call from within a .then() handler.

For example, you can do this:

function driver() {
    return clickGetTasksButton().then(() => {
        waitForSpinner().then((result) => {
            //Script should either accept task or repeat the whole process
            if (taskWaiting) {
                return processTasks();
            } else {
                return driver();
            }
        });
    });
}

or flattened a bit:

function driver() {
    return clickGetTasksButton().then(waitForSpinner).then(result => {
        //Script should either accept task or repeat the whole process
        if (taskWaiting) {
            return processTasks();
        } else {
            return driver();
        }
    });
}

Also note that clickGetTasksButton does not need to return a promise as it appears to be entirely synchronous. Also, you can't directly call .click() on the results of document.getElementsByClassName(). That function returns an array-like list (an HTMLCollection object) that doesn't have a .click() method. I don't know whether you intend to get the first DOM element in that list and call .click() on it or if you want to loop through the list calling .click() on each one.

And, your setInterval() should probably use a bit longer interval than 50ms. You want to give some cycles to the webpage itself to do its job. You're kind of hammering it here 20 times per second. I'd say set it to something like 200. And, hopefully this doesn't take long to run because if this is a mobile device, this will hammer battery life and maybe even get pre-empted by the host browser (to save battery).

Upvotes: 2

Related Questions