提问者:小点点

使用javascript,如何等待所有.toblob函数完成后再继续?


我正在使用JavaScript处理图像。对于每个图像,我使用.toblob创建4个文件。问题在于.toblob是异步的,这样一个.toblob进程可以在其他进程完成时运行。await异步似乎不起作用。处理的图像数的计数器似乎不起作用,因为上一个图像可以处理,而前一个图像尚未完成。最后一个图像将计数器增加到图像数并触发保存。

我可以在最后一个图像上做一个setTimeout,但那只是在猜测最大时间。

代码如下:

let kpData;
let repositoryData;
let collection;
let skuString = `,`;
let numImages;
let numImagesProcessed;
$(document).on(`click`, `.saveSkuImages`, function() {
    numImagesProcessed = 0;
    kpData = new FormData();
    repositoryData = new FormData();
    skuString = `,`;
    const skuContainer = $(this).closest(`.skuContainer`);
    const sku = skuContainer.attr(`sku`);
    numImages = skuContainer.find(`.skuImgContainer`).length;
    let i = 0;
    skuContainer.find(`.skuImgContainer`).each(function() {
        i++;
        sic = $(this);
        const skuImg = sic.find(`.skuImg`);
        const imgContainer = skuImg.find(`.imgContainer`);
        const img = imgContainer.find(`img:first`);
        cvs = $(`#${img.attr(`forcanvas`)}`);
        imgNum = parseInt(skuImg.attr(`imgnum`));
        const filename = `${sku}${imgNum > 1 ? `-alt${imgNum}` : ``}.jpg`;
        img.attr(`filename`, filename);
        if (cvs.length) { 
            createImages(cvs[0], imgNum, filename,i);
        } else { //if an image already exists, we didn't create a canvas for it and we don't need to recreate it.
            numImagesProcessed++;
        }
        if (sic.find(`.useForSeasonal`).is(`:checked`)) {
            kpData.append(`parentImage`, filename);
        }
    });
});

sgiArr = [`L`, `LI`, `I`, `K`, `Y`];

function createImages(loadedData,imgNum,filename,i) {
    const mime_type = `image/jpeg`;
    var cvs = document.createElement(`canvas`);
    //Create the detail version of the image
    cvs.width = 800;
    cvs.height = 800;
    ctx = cvs.getContext(`2d`);
    ctx.drawImage(loadedData, 0, 0, 800, 800);
    if (imgNum === 1 && sgiArr.indexOf(filename.split(`-`)[2]) >= 0) {
        // attach the size watermark to our primary image if it's a kid's product
        let watermark = document.getElementById(`sgi${filename.split(`-`)[2]}`);
        ctx.drawImage(watermark, 10, 720);
    }

    const newImageData = cvs.toDataURL(mime_type);
    const result_image_obj = new Image();
    result_image_obj.src = newImageData;
    if (imgNum === 1 && sgiArr.indexOf(filename.split(`-`)[2]) >= 0) {
        // display the image if we've attached the size watermark to it
        $(`img[filename="${filename}"]`).attr(`src`, result_image_obj.src);
    }
    cvs.toBlob((blob) => {
        let file = new File([blob], filename, { type: `image/jpeg` });
        kpData.append(`detail`, file, filename);
    }, `image/jpeg`,0.96);

    //create the general image
    cvs.width = 370;
    cvs.height = 370;
    cvs.getContext(`2d`).drawImage(loadedData, 0, 0, 370, 370);
    cvs.toDataURL(mime_type);
    cvs.toBlob((blob) => {
        let file = new File([blob], filename, { type: `image/jpeg` });
        kpData.append(`general`, file, filename);
    }, `image/jpeg`,0.96);

    //create the thumbnail
    cvs.width = 240;
    cvs.height = 240;
    cvs.getContext(`2d`).drawImage(loadedData, 0, 0, 240, 240);
    cvs.toDataURL(mime_type);
    cvs.toBlob((blob) => {
        let file = new File([blob], filename, { type: `image/jpeg` });
        kpData.append(`thumb`, file, filename);
    }, `image/jpeg`,0.96);

    //create the repository image for Amazon, Zulilly, Zappos and our wholesale customers. Zullily has the greatest minimum requirements so we'll use those for everyone
    loadedData.toBlob((blob) => {
        let file = new File([blob], filename, { type: `image/jpeg` });
        repositoryData.append(`imgfiles`, file, filename);
        numImagesProcessed++;
        console.log(`repository data created: `, numImagesProcessed, imgNum);
        if (numImagesProcessed === numImages) { // the process can get to here yet not all the previous .toBlob statments from previous images may have completed.
            console.log(`finished. Trigger save`);
            saveit();
        }
    }, `image/jpeg`, 0.92);
}

我试过用一系列的承诺:

skuContainer.find(`.skuImgContainer`).each(function() {
    promises.push(processSku($(this),sku));
});
$.when.apply($, promises).done(function() {
    console.log(`promises are done:`,numFiles, numFilesCreated);
    saveit();
});

那就更糟了。done函数甚至在第二次迭代完成之前就启动了。我尝试在每个.toblob函数中递增一个计数器,但计数器没有在“if(counter===NumFileStocreate)saveIt();”的时间内递增

我不知所措。

谢谢


共1个答案

匿名用户

您很可能希望使用promise.all方法。我知道你在评论中说它不起作用,但我想展示一个起作用的例子。如果这在您的实例中不起作用,那么为了在这个论坛上获得帮助,创建一个演示您的确切问题的MRE可能是最有益的。一些可以运行的东西。

null

var promisesArray = [
  new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('promise 1 done!');
      resolve('');
    }, 300);
  }),
  new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('promise 2 done!');
      resolve('');
    }, 5);
  }),
  new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('promise 3 done!');
      resolve('');
    }, 50);
  }),
  new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('promise 4 done!');
      resolve('');
    }, 500);
  })
];

async function ResolveAll() {
  await Promise.all(promisesArray);
  console.log('after all promises');
}

ResolveAll();