meridius
meridius

Reputation: 1587

Javascript promises stuck waiting for each other

I have this code for managing dashboard which contains approximate 100 of independent checks.

Check results are received via AJAX call.

There is one initial request for each check at start. After result is received for particular check, the code recursively waits for set timeout and repeats the request again for that check again.

One promise = one check.

I am wondering why promises starts to resolve only after each one of them is pending (none of them is in timeout period). And that is even if response from server is "instantaneous", they just wait for the last promise in cycle.

const TIMEOUT = 4000;

function checkForUpdate(environment, application, check) {
    Dashboard.setCheckPending(environment, application, check);

    return Communicator.getStatus(environment, application, check)
        .then(status => {
            Dashboard.updateCheckCell(environment, application, check, status);
            Dashboard.updateEnvironmentCell(environment, application);

            setTimeout(() => {
                    return checkForUpdate(environment, application, check)
                },
                TIMEOUT
            );
        });
}


Communicator.getEnvMatrix()
    .then(data => {
        Dashboard.create(data);

        $.each(data, (environment, applications) => {
            $.each(applications, (application, checks) => {
                $.each(checks, (key, check) => {
                    checkForUpdate(environment, application, check);
                });
            });
        });
    });

The question is also how to rewrite that so each of the checks waits just for its own result to be delivered and for set timeout.


EDIT (clarification):

Each of the 100 checks are independent, that is why I want to run AJAX for each of them as soon as I can (inside the $.each() loops).

The check is dependent only on itself. I don't want it to wait on any other check.

After the result of a check is received it has to wait for set timeout before it tries to retrieve its status again. That is why I encapsulated the recursive function within the setTimeout().

Even if I rewrite (see below) the setTimeout() as promise, the behavior stays the same unfortunately.

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


function checkForUpdate(environment, application, check) {
    Dashboard.setCheckPending(environment, application, check);

    let promise = Communicator.getStatus(environment, application, check).promise();

    return promise
        .then(status => {
            Dashboard.updateCheckCell(environment, application, check, status);
            Dashboard.updateEnvironmentCell(environment, application);

            return delay(TIMEOUT).then(() => {
                return checkForUpdate(environment, application, check);
            });
        });
}

Upvotes: 0

Views: 130

Answers (1)

jfriend00
jfriend00

Reputation: 707238

Your code runs $.each() synchronously. That means it is going to call every checkForUpdate() before anything else can run. Since standards-conforming promises are always resolved asynchronously (on some future tick), that means that every single request here will get started before ANY promise can run its .then() handler. That's how promises work. Only once the $.each() loop is done can the Javascript interpreter start to process the .then() handlers of resolved promises.

Also, it is unclear why you are trying to do a return checkForUpdate(environment, application, check) inside the setTimeout(). The return there does nothing. It's just returning to the setTimeout() callback which does nothing. The parent function has long since already returned so this is not chaining the next checkForUpdate() to the previous promise chain. If you wanted to chain them together, then you need to make a delay with a promise and return that promise like is shown in these references:

using setTimeout on promise chain

Delays between promises in promise chain

Delay chained promise


The unexpected thing is that even if all 100 requests are sent immediately after page loads, they are being resolved just few at a time and (roughly) in the order they've been sent. Roughly means 3, 2, 5, 1, 8, .... But I'd expect something like 3, 89, 12, 76, 21, 94, .... There seems to be some limit on how many promises can be run concurrently and in what order.

Another thing that will influence your ajax calls is that each browser has a limit on how many concurrent ajax calls it will allow to the same host. If you exceed that limit, it will queue them and not run subsequent ones until some earlier ones finish. Each browser sets its own limit and they've changed over time so I don't know exactly what the current limits are, but they are lowish. I know Chrome used to be something like 6 at at time to the same host. So, that will also affect the exact order that things complete.

When you hit this limit, Ajax calls will be sent in the order they were called by your code. So, if the limit was 6 per host, then your first 6 would be sent and the 7th request would only be sent when one of the first 6 finished and so on. That still doesn't guarantee a finish order, but it does affect the ability for a later request to finish before an earlier request.

Upvotes: 2

Related Questions