Javascript for循环,等待函数回调

时间:2015-11-21 04:17:40

标签: javascript jquery asynchronous

我有一个for循环,它自己调用一个函数。该函数有一个回调函数,但for循环不会等待它完成并继续工作。

for循环实际上完成得非常快,以至于在第一个函数调用完成之前完成了下一个操作,该操作服务于应该在for循环中填充的文档。

以下是我实际使用的通话内容,其中图片是一个包含图片外部网址的数组:

// New jsPDF Document
var doc = new jsPDF();

doc.setFontSize(12);

// For each image we add the image to the document as an image
for (var index = 0; index < images.length; index++) {

    // We only add pages after the first one
    if (index !== 0) {
        doc.addPage();
    }

    // This puts the URL of the active element at the top of the document
    doc.text(35, 25, images[index].path);

    // Call to our function, this is the 'skipped' portion
    convertImgToDataURLviaCanvas(images[index].path, function(base64Img) {
        console.log('An image was processed');
        doc.addImage(base64Img, 15, 40, 180, 180);
    });
}

doc.save('demo.pdf');
console.log('Document served!');

我们从数组中获取图片网址,然后添加所有内容。 convertImgToDataURLviaCanvas函数在这里:

// Gets an URL and gives you a Base 64 Data URL
function convertImgToDataURLviaCanvas(url, callback, outputFormat){
    var img = new Image();
    img.crossOrigin = 'Anonymous';
    img.onload = function() {
        var canvas = document.createElement('CANVAS');
        var ctx = canvas.getContext('2d');
        var dataURL;
        canvas.height = this.height;
        canvas.width = this.width;
        ctx.drawImage(this, 0, 0);
        dataURL = canvas.toDataURL(outputFormat);
        callback(dataURL);
        canvas = null; 
    };
    img.src = url;
}

在前面的示例中,即使行doc.text(35, 25, images[index].path);也能正确地将URL写入页面顶部。因为它包含在数组中并沿着迭代器工作。但是,for循环在将第一个图像添加到我们的文档之前完全完成!

使用console.log,您会在第一个'Document served!'之前看到'An image was processed'。我们的目标是在'An image was processed'出现之前输出Document served!每个rake db:drop db:create db:migrate db:seed

如何实现此功能?

1 个答案:

答案 0 :(得分:4)

保证图片的顺序,使用promises很简单

var promises = images.map(function(image, index) {
    return new Promise(function(resolve) {
        convertImgToDataURLviaCanvas(image.path, function(base64Img) {
            resolve(base64Img);
        });
    });
}
Promise.all(promises).then(function(results) {
    results.forEach(function(base64Img, index) {
        if (index !== 0) {
            doc.addPage();
        }
        doc.text(35, 25, images[index].path);
        console.log('An image was processed');
        doc.addImage(base64Img, 15, 40, 180, 180);
    });
    doc.save('demo.pdf');
    console.log('Document served!');
});

为了完整性(虽然未经检查) - 保证图像顺序的非承诺方式,并将addPage /文本放在正确的位置

var base64 = new Array(images.length); // base64 images held here
images.forEach(function(image, index) {
    // Call to our function, this is the 'skipped' portion
    convertImgToDataURLviaCanvas(image.path, function(base64Img) {
        console.log('An image was processed');
        // We only add pages after the first one
        base64[index] = base64Img;
        done++;
        if (done == images.length) {
            base64.forEach(function(img64, indx) {
                if (indx !== 0) {
                    doc.addPage();
                }
                // This puts the URL of the active element at the top of the document
                doc.text(35, 25, images[indx].path);
                doc.addImage(img64, 15, 40, 180, 180);
            }
            doc.save('demo.pdf');
            console.log('Document served!');
        }
    });
}

一种方法如下

var done = 0; // added this to keep counf of finished images
for (var index = 0; index < images.length; index++) {

    // We only add pages after the first one
    if (index !== 0) {
        doc.addPage();
    }

    // This puts the URL of the active element at the top of the document
    doc.text(35, 25, images[index].path);

    // Call to our function, this is the 'skipped' portion
    convertImgToDataURLviaCanvas(images[index].path, function(base64Img) {
        console.log('An image was processed');
        doc.addImage(base64Img, 15, 40, 180, 180);
        // added this code, checks number of finished images and finalises the doc when done == images.length
        done++;
        if (done == images.length) {
            // move the doc.save here
            doc.save('demo.pdf');
            console.log('Document served!');
        }
        // end of new code
    });
}

使用承诺,它很容易......

// For each image we add the image to the document as an image
var promises = images.map(function(image, index) {
    // We only add pages after the first one
    if (index !== 0) {
        doc.addPage();
    }

    // This puts the URL of the active element at the top of the document
    doc.text(35, 25, image.path);

    // Call to our function, this is the 'skipped' portion
    // this returns a Promise that is resolved in the callback
    return new Promise(function(resolve) {
        convertImgToDataURLviaCanvas(image.path, function(base64Img) {
            console.log('An image was processed');
            doc.addImage(base64Img, 15, 40, 180, 180);
            resolve(index); // doesn't really matter what is resolved
        });
    });
}
Promise.all(promises).then(function(results) {
    doc.save('demo.pdf');
    console.log('Document served!');
});

<击>