javascriptnode.jsfile-processing

Can't get the program to pause before executing next function in NodeJS


I can't get the program execution to pause so that all files are loaded before moving to another function. Basically, in my program, path of all images are extracted. Then, it is resized and compressed. My issue is, program moves to compress before it finished resizing. Please take a look and recommend some solutions.

const path = require('path');
const fs = require('fs');
const jimp = require('jimp');
const compress_images = require('compress-images');

// gets the names of all the images in the folder
// accepts the name of the folder in arguments
function getImagesPath (directory) {
    const directoryPath = path.join(__dirname, directory);
    const images = fs.readdirSync(directoryPath);

    images.forEach(function (image, i) {
        images[i] = path.join(__dirname, directory, image);
    })

    return images;
}

// function to check if the image date and current date is same
function isSameDate(path, currentDate) {
    const imgDate = fs.statSync(path).birthtime.toDateString();
    
    if (imgDate === currentDate) {
        return true;
    } else {
        return false;
    }
}

// function to check if the image is less than limit
function checkSize(path) {
    const limit = 300000 // 300kb in bytes
    const imgSize = fs.statSync(path).size;
    
    if (imgSize < limit) {
        return false
    } else {
        return true
    }
}

// resizes the images
// receives the name of the images and the save folder in arguments
async function resizeImages(images, saveFolder) {
    const shrinkPercentage = 60;
    const directoryPath = path.join(__dirname, saveFolder);
    const currentDate = new Date(Date.now()).toDateString();

    await images.forEach(async function (image)  {

        // // if dates don't match; current iteration is skipped
        // if (isSameDate(image, currentDate) === false) {
        //     return;
        // }

        // if the image is smaller than the limit; current iteration is skipped
        if (checkSize(image) === false) {
            return;
        }

        // reading the image
        const img = await jimp.read(image);

        // setting the dimensions
        let width = img.bitmap.width - (img.bitmap.width * shrinkPercentage/100);
        let height = img.bitmap.height - (img.bitmap.height * shrinkPercentage/100);

        // resizing the image
        if (width < 732) {
            width = 732;
            await img.resize(width, jimp.AUTO);    // Jimp.AUTO can be used for height or width (not for both) for automatic scaling
        } else {
            await img.resize(width, height);
        }

        // saving the resized image
        const newImageName = path.parse(image).name + `-${width}X${height}` + path.parse(image).ext;
        await img.write(`${directoryPath}/${newImageName}`);
    })
}

// compresses the image
async function compressImages(inputFolder, outputFolder) {

    compress_images(inputFolder, outputFolder, { compress_force: true, statistic: true, autoupdate: true }, false,
        { jpg: { engine: "mozjpeg", command: ["-quality", "60"] } },
        { png: { engine: "pngquant", command: ["--quality=20-50", "-o"] } },
        { svg: { engine: false} },
        { gif: { engine: false} },
        function (error, completed, statistic) {
            console.log("-------------");
            console.log(error);
            console.log(completed);
            console.log(statistic);
            console.log("-------------");
        }
    );
}

// main function
async function main() {
    //resizes the images
    let images = getImagesPath('original');
    await resizeImages(images, 'resized');

    // compress the images
    await compressImages('resized/**/*.{jpg,JPG,jpeg,JPEG,png}', 'compressed/');    
}

main();

Solution

  • forEach does not return anything, so await does nothing on it. You can make an array of promises and use Promise.all to await them.

    const promises = images.map(async () => {
        // ...
        return img.write(`${directoryPath}/${newImageName}`);
    });
    
    await Promise.all(promises);
    

    Minimum example:

    const fn = async () => {
      const promises = [1, 2, 3].map(async(v) => {
        // Simulates slow op
        await new Promise((r) => setTimeout(r, v * 100));
        return v * 10;
      });
    
      return Promise.all(promises);
    }
    
    (async() => {
      const data = await fn();
      console.log('fn done with ', data);
    })();