仅在 javascript 循环完成后执行 ajax 调用

Posted

技术标签:

【中文标题】仅在 javascript 循环完成后执行 ajax 调用【英文标题】:Execute ajax call only after javascript loop has finished 【发布时间】:2015-06-26 22:15:57 【问题描述】:

下面是我使用的代码,它基本上传递了多个要上传的文件。在循环中,每个文件都在客户端调整大小,然后上传。

我想在循环完成上传照片后执行 ajax 调用。 ajax 调用基本上是重新加载特定的 div 并刷新照片。

如何防止 ajax 调用在循环完成之前执行。

if (window.File && window.FileReader && window.FileList && window.Blob)
           
        var files = document.getElementById('filesToUpload').files;    
        for(var i = 0; i < files.length; i++) 
        
            resizeAndUpload(files[i]);


        

        // when loop finished, execute ajax call
            $.ajax
            (
                type: "POST",
                url: "photos.php",
                data: dataString,
                success: function(html)
                
                    $("#photo-body").html(html);
                       
            );
               
    

function resizeAndUpload(file)

    var reader = new FileReader();
    reader.onloadend = function() 
    
        var tempImg = new Image();
        tempImg.src = reader.result;
        tempImg.onload = function()
        
            var MAX_WIDTH = 382.25;
            var MAX_HEIGHT = 258.5;
            var tempW = tempImg.width;
            var tempH = tempImg.height;

            if (tempW > tempH) 
            
                if (tempW > MAX_WIDTH)
                
                    tempH *= MAX_WIDTH / tempW;
                    tempW = MAX_WIDTH;
                
             
            else
            
                if (tempH > MAX_HEIGHT)
                
                    tempW *= MAX_HEIGHT / tempH;
                    tempH = MAX_HEIGHT;
                
            

            var canvas = document.createElement('canvas');
            canvas.width = tempW;
            canvas.height = tempH;
            var ctx = canvas.getContext("2d");
            ctx.drawImage(this, 0, 0, tempW, tempH);
            var dataURL = canvas.toDataURL("image/jpeg");

            var xhr = new XMLHttpRequest();
            xhr.onreadystatechange = function(ev)
            
                document.getElementById('filesInfo').innerHTML = 'Upload Complete';

            ;
            xhr.open('POST', 'upload-resized-photos.php', true);
            xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
            var data = 'image=' + dataURL;
            xhr.send(data); 


        
    
    reader.readAsDataURL(file);


var _validFileExtensions = [".jpg", ".jpeg", ".bmp", ".gif", ".png"];    
function Validate(oForm)

    var arrInputs = oForm.getElementsByTagName("input");
    for (var i = 0; i < arrInputs.length; i++) 
    
        var oInput = arrInputs[i];
        if (oInput.type == "file")
        
            var sFileName = oInput.value;
            if (sFileName.length > 0)
            
                var blnValid = false;
                for (var j = 0; j < _validFileExtensions.length; j++) 
                
                    var sCurExtension = _validFileExtensions[j];
                    if (sFileName.substr(sFileName.length - sCurExtension.length, sCurExtension.length).toLowerCase() == sCurExtension.toLowerCase()) 
                    
                        blnValid = true;
                        break;
                    
                

                if (!blnValid) 
                
                    alert("Sorry, " + sFileName + " is invalid, allowed extensions are: " + _validFileExtensions.join(", "));
                    return false;
                   
            
        
    
    return true;

【问题讨论】:

你需要从resizeAndUpload返回promise。然后使用$.when() 等待所有的promise 完成。 你能告诉我如何在上面的代码中做到这一点。非常感谢 可以添加resizeAndUpload的代码吗? 修改了上面的帖子以包含功能 【参考方案1】:

您可以将 $ajax 调用包装在一个函数中,并在最终循环结束时调用该函数。

(只是脚本的顶部)

if (window.File && window.FileReader && window.FileList && window.Blob)        

    function loopFinished()
        $.ajax
        (
            type: "POST",
            url: "photos.php",
            data: dataString,
            success: function(html)
            
                $("#photo-body").html(html);
                   
        );
    

    var files = document.getElementById('filesToUpload').files;    
    for(var i = 0; i < files.length; i++) 
    
        resizeAndUpload(files[i]);
        if (files.length+1 == [i])
            loopFinished();
        
    
 

【讨论】:

【参考方案2】:

您可以使用任何 Promise 库来执行此操作。这是使用 jQuery Promise 的示例

     (function ($) 
        var files = [1, 2, 3, 4],
            allPromises = [];
        for (var i = 0; i < files.length; i++) 
            var promise = resizeAndUpload(files[i]);
            allPromises.push(promise);

        

        $.when.apply($, allPromises).done(function () 
            makeAjaxCall();
        );

        function makeAjaxCall() 
            console.log('Put Ajax call here');
        

        function resizeAndUpload(file) 
            var defer = $.Deferred();
//Set timeout simulates your long running process of processing file
            setTimeout(function () 
                console.log('processing file ' + file);
                defer.resolve();

            , 2000);
            return defer.promise();
        
    )(jQuery);

这是一个 jSFiddle http://jsfiddle.net/x6oh471f/2/

【讨论】:

【参考方案3】:

resizeAndUpload() 函数中的一个或多个方法必须异步发生。这意味着他们将在您的其余 javascript 运行时在后台做他们的事情,并且他们应该在完成时触发一个事件。一旦这些方法中的最后一个完成并触发了事件,您将需要调用 ajax 方法。例如,fileReader 方法是异步的。这意味着您可能需要执行以下操作:

FileReader.onloadend = function()
   totalFilesLoaded = totalFilesLoaded + 1;
   if (totalFilesLoaded == files.length)
      //all files have been uploaded, run $ajax
   

编辑:现在您已经上传了其余代码,请尝试以下操作:

window.totalFilesLoaded = 0;
var files = document.getElementById('filesToUpload').files;
window.totalFilesToLoad = files;

if (window.File && window.FileReader && window.FileList && window.Blob)
           

        for(var i = 0; i < files.length; i++) 
        
            resizeAndUpload(files[i]);
        

    

单独的ajax函数:

window.runAjax = function()
           $.ajax
            (
                type: "POST",
                url: "photos.php",
                data: dataString,
                success: function(html)
                
                    $("#photo-body").html(html);
                       
            );


function resizeAndUpload(file)

    var reader = new FileReader();
    reader.onloadend = function() 
    

    ...

            xhr.onreadystatechange = function(ev)
            
                document.getElementById('filesInfo').innerHTML = 'Upload Complete';


                window.totalFilesLoaded++;
                if (window.totalFilesLoaded == window.totalFilesToLoad.length)
                    window.runAjax()
                


            ;

    ...

    
    reader.readAsDataURL(file);

【讨论】:

我会在哪里添加该代码。我尝试在 for 循环之后添加它。文件上传,但 ajax 确实被调用。我也在进入循环之前设置了 totalFilesLoaded = 0。 runAjax 永远不会被执行。如果我添加一个警报(files.length),我会得到未定义的。 抱歉,文件也需要是全局的,将 var files = document.getElementById('filesToUpload').files; 声明放在顶部,在带有 totalFilesLoaded 的 if 语句之外 - 我再次编辑了我的回复 - 抱歉,我没有时间测试我自己 发生了一些奇怪的事情。我有上面的一切。如果我在 totalFilesLoaded 下放置一个警报(totalFilesLoaded),则不会发出任何警报。同样由于某种原因, runAjax 从未被调用。我迷路了。 这很奇怪,你的意思是你直接在下面提醒它var totalFilesLoaded = 0;?并且根本没有出现警报框?你可能想把它放在jsfiddle 或类似的地方。

以上是关于仅在 javascript 循环完成后执行 ajax 调用的主要内容,如果未能解决你的问题,请参考以下文章

仅在函数执行完成后打印 [重复]

如何仅在 Windows 服务完成后继续执行代码?

仅在第一个功能完成后才执行功能 - ios

仅在执行所有 ajax 脚本时刷新页面

如何在 Ajax 加载后执行 javascript?

仅在元素完全加载时执行一次的函数