Why this code does not download 25 images, but instead 15 to 20?

Viewed 75

I would like to cut an image into small images, in order to make a puzzle. I write the image url, the size of the canvas which is the same as the image, then I reload the page, and I should have 25 cropped images of the big image.

The issue is that it does not download 25 images, sometimes it jumps from image19 to image25. I have to reload the page again, multiple times, to have all the images. Would you know why? Is it because the function is directly called here :

  promises.push(getCanvas(canvas, j*w, i*h, w, h, countImg));

?

In order to test it, you need to use a local server like MAMP (mac). First we load the big image in a canvas, then we store each promise that will take a small part of the big image, and we use Promise.all . The console.log("test") shows up 25 times. But I only have 15 to 20 images downloaded.

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>

    <canvas width="560" height="647" id="test"></canvas>
    <img src="#" id="captured" alt="">
    
    <script>
        let url = 're.png'; //the big image
        let tab = new Array();
        let countImg = 0, countClick = 0, row = 5;
        var canvas = document.getElementById('test');
        var ctx = canvas.getContext('2d');
        
        let promises = new Array();

        fillWithImage();

        function fillWithImage(){ //fill the canvas with the big image
            var base_image = new Image();
            base_image.src = url;
            base_image.crossOrigin = "Anonymous";
            base_image.onload = function(){
                ctx.drawImage(base_image, 0, 0);

                let cut = row;
                let w = Math.floor(base_image.width / cut); //we cut the big image in small parts
                let h = Math.floor(base_image.height / cut);
                for (let i=0; i<cut; i++){
                    for (let j=0; j<cut; j++){
                        countImg++;
                        promises.push(getCanvas(canvas, j*w, i*h, w, h, countImg));
                    }
                }
                Promise.all(promises).then(() => console.log('All transaction requests resolved'))
            }
        }

        function getCanvas(canvas, posX, posY, width, height, index){ //we cut a small part
            return new Promise((resolve, reject) => { 
                var context = canvas.getContext('2d');
                var imageData = context.getImageData(posX, posY, width, height).data;
                var outputCanvas = document.createElement('canvas');//2nd canvas created
                var outputContext = outputCanvas.getContext('2d');
                outputCanvas.width = width;
                outputCanvas.height = height;

                var idata = outputContext.createImageData(width, height);
                idata.data.set(imageData);
                outputContext.putImageData(idata, 0, 0);

                var anchor = document.createElement("a");
                console.log("test");//prints 25
                anchor.href = outputCanvas.toDataURL("image/png");
                anchor.download = "IMAGE"+index+".PNG";
                anchor.click();
                resolve("ok");
            });

        }
    </script>
</body>
</html>

EDIT :

So based on your code, it downloads "IMAGE25.png" 13 times, or a random number of times :

    function fillWithImage(){
        var base_image = new Image();
        base_image.src = url;
        base_image.crossOrigin = "Anonymous";
        base_image.onload = function(){
            ctx.drawImage(base_image, 0, 0);

            let cut = row;
            let w = Math.floor(base_image.width / cut);
            let h = Math.floor(base_image.height / cut);

            let fns = [];
            for (let i=0; i<cut; i++){
                for (let j=0; j<cut; j++){
                    countImg++;
                    fns.push(() => getCanvas(canvas, j*w, i*h, w, h, countImg));
                }
            }
            async function goAhead (fnsToExecute) {
                for (let i=0; i < fns.length; i++) {
                    await fnsToExecute[i]();
                }
            }
            goAhead(fns).then(() => console.log('All transaction requests resolved'))
        }
    }
1 Answers

The code itself is fine, you can add some "console logs" or debug it to see what actually happens. Also you can add "try-catch" and log any error you catch during the getCanvas function.

const row = 5
const promises = [];
let countImg = 0;
let cut = row;
                let w = Math.floor(500 / cut); //we cut the big image in small parts
                let h = Math.floor(500 / cut);
                for (let i=0; i<cut; i++){
                    for (let j=0; j<cut; j++){
                        countImg++;
                        promises.push(getCanvas({}, j*w, i*h, w, h, countImg));
                    }
                }

Promise.all(promises).then(() => console.log('All transaction requests resolved'))

                
function getCanvas(canvas, posX, posY, width, height, index){
  return new Promise((resolve) => {
    console.log(index, posX, posY, width, height);
    resolve("ok");
  });
}

What can be potential issue is just too much requests at once (then some of them can brake). Try to split them to smaller chunks (i.e. 5 at once) and await the chunk to finish before you continue with new one.

If you want a better (but maybe a bit more difficult to understand and use) for this, you can use: https://caolan.github.io/async/v3/parallelLimit.js.html

You put there array of functions that create promises (not promises itself) and it will keep executing only certain limit of them. Once one of it finishes it starts another one.

This is simple example of creating array of functions which you can later execute in a way you like. My example is just doing one-by-one, but you can update that function to take i.e. chunks by 5 and await them all at once.

const row = 5
const promises = [];
const fns = [];
let countImg = 0;
let cut = row;
                let w = Math.floor(500 / cut); //we cut the big image in small parts
                let h = Math.floor(500 / cut);
                for (let i=0; i<cut; i++){
                    for (let j=0; j<cut; j++){
                        countImg++;
                        fns.push(() => getCanvas({}, j*w, i*h, w, h, countImg));
                    }
                }

async function goAhead (fnsToExecute) {
    for (let i=0; i < fns.length; i++) {
       await fnsToExecute[i]();
    }
}

goAhead(fns).then(() => console.log('All transaction requests resolved'))

                
function getCanvas(canvas, posX, posY, width, height, index){
  return new Promise((resolve) => {
    console.log(index, posX, posY, width, height);
    resolve("ok");
  });
}

Related