skip to Main Content

I have the following class that I’m using to route all my API requests through

class PromiseBroker {
    #promises = {};
    #overlayEle = document.getElementById('loading-overlay');
    constructor(){

    }
    get promises(){
        return this.#promises;
    }
    setPromise(key, value){
        this.promises[key] = value;
        this.#overlayEle.classList.remove('d-none');
        Promise.allSettled(Object.values(this.promises)).then(results => {
            this.#overlayEle.classList.add('d-none')
        });
    }
}

The purpose of this class is to dismiss the loading overlay once all API requests are finished. I don’t know how many requests will be made ahead of time. The issue I’m having is that as promises are added, it will be dismissed prematurely. Here is what I’m referring to:

  1. Promise A is added – loading overlay is added
  2. Promise A resolves – loading overlay is dismissed
  3. Promise B is added – loading overlay is added
  4. Promise C is added
  5. Promise B resolves – loading overlay is dismissed
  6. Promise C resolves – here is when I want the overlay to be dismissed

I would want to dismiss only when all promises (including ones that have been added since the allSettled call) have been resolved. The issue, I’m assuming, is that the state changes while awaiting the promises to settle. I don’t know if there is a way to cancel or overwrite the allSettled to only use the most recent call.

2

Answers


  1. Store the current promises in a var, check if the promises are still the same before dismissing the loading overlay.

    class PromiseBroker {
        #promises = {};
        #overlayEle = document.getElementById('loading-overlay');
      
        get promises() {
            return this.#promises;
        }
    
        setPromise(key, value) {
            const currentPromises = { ...this.promises }; // copy - important
            currentPromises[key] = value;
            this.#overlayEle.classList.remove('d-none');
            
            Promise.allSettled(Object.values(currentPromises)).then(results => {
                if (currentPromises === this.promises) {
                    this.#overlayEle.classList.add('d-none');
                }
            });
            this.#promises = currentPromises; // we can update after the checks
        }
    }
    
    Login or Signup to reply.
  2. Don’t use allSettled at all. Watch the completion of each individual promise only once, and keep a collection of (keys for) promises that are pending:

    const defaultUpdate = activeCount => {
        const overlay = document.getElementById('loading-overlay');.
        overlay.classList.toggle('d-none', activeCount == 0);
    };
    
    class PromiseBroker {
        #promises = {};
        #active = new Set();
        #update;
        constructor(update = defaultUpdate) {
             this.#update = update;
        }
        get promises(){
            return this.#promises;
        }
        get activeCount() {
           return this.#active.size;
        }
        setPromise(key, value) {
            this.promises[key] = value;
            this.#active.add(key);
            this.#update(this.activeCount);
            const done = () => {
                this.#active.delete(key);
                this.#update(this.activeCount);
            };
            Promise.resolve(value).then(done, done);
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search