Search code examples
javascriptooppromisefetch

Execute a new set of fetches only after the earlier set has ran trough


I'm trying to fetch images for a live camera feed web page from an API. The problem is that with just timed fetch request, eventually one of the API responds takes so long that the code errors. The page has multiple camera feeds that are simultaneously refreshing with the loop for all the cameras in the cameraObjects[] array. The image() function should respond with a resolve that would then be collected in to an array promises[].

Put simply I need to run the refreshImages() function when ALL the image() functions called by the loop in updateImages() have been ran. I have just started coding so bare with me...

class camera {
    constructor(Uuid,url,username,password) {
        this.Uuid = Uuid;
        this.url = url;
        this.username = username;
        this.password = password;
    }
    image() {

        let uuid = this.Uuid
        let url = this.url
        let username = this.username
        let password = this.password
        let headers = new Headers();
        let authString = `${username}:${password}`; 
        headers.set('Authorization', 'Basic ' + btoa(authString));
        let imageUrl = url + uuid
        fetch(imageUrl,{method: 'GET', headers: headers})
            .then(response => response.blob())
            .then(image => {
                console.log(image);
                var reader = new FileReader();
                reader.readAsDataURL(image);
                reader.onloadend = function() {
                        var base64data = reader.result;                
                        let img = document.getElementById("camera_" + uuid);
                        img.src = base64data;
                        return new Promise(function(resolve) {
                                resolve(promise);
                        })

                }
            })
    }
        
} 
function updateImages() {
    cameraObjects = listOfCameraObjects();
    let promises = [];
    for(let e = 0; e < cameraObjects.length; e++) {
        let promise = new Promise(cameraObjects[e].image())
        promises.push(promise)
    }
    Promise.all(promises)
        .then(() => { 
            refreshImages();
        })
}
function refreshImages() {
    let currentInterval = getInterval();
    refrehInterval = setTimeout(updateImages, currentInterval);
    console.log(refrehInterval)
    
}

Solution

  • There a few things you're doing wrong with Promises -

    return new Promise(function(resolve) {
        resolve(promise);
    })
    

    That's sort of OK though return Promise.resolve(promise) is identical - however in your code, what is promise? not declared anywhere in that scope - also, as the last code in an event handler (onloadend) it is pointless, since returning a value from an event handler is meaningless

    let promise = new Promise(cameraObjects[e].image())
    

    that's not how you construct a promise ... the argument to the promise constructor needs to be a function, not the result of calling a function (unless that returns a function, of course, but it doesn't)

    I'd suggest you perhaps read some docs about how you construct a Promise, and how you then use them

    In the meantime, I believe this code will do what you want

    class camera {
        constructor(Uuid,url,username,password) {
            this.Uuid = Uuid;
            this.url = url;
            this.username = username;
            this.password = password;
        }
        image() {
            const headers = new Headers();
            const authString = `${this.username}:${this.password}`; 
            headers.set('Authorization', 'Basic ' + btoa(authString));
            const imageUrl = this.url + this.Uuid;
            
            return fetch(imageUrl, {method: 'GET', headers: headers})
            .then(response => response.blob())
            .then(image => new Promise((resolve, reject) {
                const reader = new FileReader();
                reader.readAsDataURL(image);
                reader.addEventListener('loadend', () => {
                    const img = document.getElementById("camera_" + uuid);
                    img.src = reader.result;
                    resolve();
                });
                reader.addEventListener('error', reject);
            }));
        }
            
    } 
    function updateImages() {
        Promise.all(listOfCameraObjects().map(cam => cam.image()))
        .then(refreshImages);
    }
    function refreshImages() {
        let currentInterval = getInterval();
        refrehInterval = setTimeout(updateImages, currentInterval);
        console.log(refrehInterval)
    }