简体   繁体   中英

How to put async-await in a loop? Not working

I am using ml5 to classify images in a machine learning project. The program takes images path and type from a json file and then it adds each image to the ml5 classifier, which is then used to train machine learning model.

When I try to load only one image into the classifier, it works normally, (however it won't create a model because minimum of 2 are required for it to train). But when I try to add these images to the classifier in a for loop, it gives me errors.

The files are loaded by the load_data() function which is triggered by a button. load_data() calls load_imgs(label, nimgs) where label is the folder and nimgs is the number of images in it. load_imgs(label, nimgs) then loops over nimgs and takes the path of the image specified in data(json file), makes an image element out of it and then adds it to the classifier.

These functions are defined as:

    async function load_imgs(label, nimgs) {

        const forLoop = async _ => {
            for (let i = 0; i < nimgs; i++) {
                const imageData = await data.children[label].children[i]
                const image = document.createElement("img")
                image.src = imageData.path
                const type = await imageData.type;
                await classifier.addImage(image, type, (res) => {
                    console.log("image added to classifier", image.height);
                    console.log(image);

                })

            }
        }
        await forLoop()
    }

    function load_data() {
        (async() => {
            try {
                await load_imgs(0, googleImages);
                await load_imgs(1, amazonImages);
                await load_imgs(2, paypalImages);
                await load_imgs(3, facebookImages);
                await load_imgs(4, dropboxImages);
                console.log("images added");

            } catch (error) {
                console.log(error);
            }
        })();

    }

Log generated for this code is:

    index-train.html:106 Live reload enabled.
    train.js:84 json loaded
    train.js:69 Model loaded
    train.js:38 image added to classifier 0
    train.js:39 <img src=​"all_data/​google/​google_85.png">​
    train.js:107 Error: Requested texture size [0x0] is invalid.
        at re (tf-core.esm.js:17)
        at Wi (tf-core.esm.js:17)
        at Gi (tf-core.esm.js:17)
        at t.createUnsignedBytesMatrixTexture (tf-core.esm.js:17)
        at t.acquireTexture (tf-core.esm.js:17)
        at t.acquireTexture (tf-core.esm.js:17)
        at t.uploadToGPU (tf-core.esm.js:17)
        at t.getTexture (tf-core.esm.js:17)
        at t.fromPixels (tf-core.esm.js:17)
        at t.fromPixels (tf-core.esm.js:17)

I expect this to print the actual size of the image in the function callback of classifier.addImage but instead, it is getting a 0x0 texture

Reading image.height immediately after setting image.src results in getting 0 as height. After setting src you should wait for the image to load. There's no promise to await for loading the image. Instead it does have a callback called onload :

let img = new Image();
img.onload = function() { console.log("Height: " + this.height); }
img.src = "...";

Try to use the map function instead of for loop, convert nimgs into array and use map function like this:

async function load_imgs(label, nimgs) {

    await Promise.all(nimgs.map(async (i) => {
        const imageData = await data.children[label].children[i]
        const image = document.createElement("img")
        image.src = imageData.path
        const type = await imageData.type;
        await classifier.addImage(image, type, (res) => {
                console.log("image added to classifier", image.height);
                console.log(image);

        })
    }))
}

See this explanation: Using async/await with a forEach loop

See this article fro more infos: https://lavrton.com/javascript-loops-how-to-handle-async-await-6252dd3c795/

What you suggest is a declared bad practice.

Is not a good practice to "put awaits inside loops".

Theorics are that you must not to await async operations in every iteration of the loop. Instead you must declare all async operations, then await.

This example from ES Lint I think fills your scenario.

Examples of correct code for this rule:

async function foo(things) {
  const results = [];
  for (const thing of things) {
    // Good: all asynchronous operations are immediately started.
    results.push(bar(thing));
  }
  // Now that all the asynchronous operations are running, here we wait until they all complete.
  return baz(await Promise.all(results));
}

Examples of incorrect code for this rule:

async function foo(things) {
  const results = [];
  for (const thing of things) {
    // Bad: each loop iteration is delayed until the entire asynchronous operation completes
    results.push(await bar(thing));
  }
  return baz(results);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM