简体   繁体   中英

Updating global variable via callback

I have the following function

   var urls = '';
    handleFiles(f,function(url){
        urls = urls + url + ',';
        console.log("urls is " + urls);
    });

I get the url after uploading and update my urls. But my Urls never gets updated, it shows the url of the last file uploaded.

UPDATE 1 This is my complete code now.

var urls = '';
document.getElementById('question-file-selector').addEventListener('change',handleFileSelect,false);
    function handleFileSelect(evt){
        var files = evt.target.files; //File list object
            // Loop through file list, render image files as thumbnails
        for (var i = 0,f;f = files[i];i++){


        handleFiles(f,function(url){
                urls = urls + url + ',';
                console.log("urls is " + urls);
                });
        // Only process image files
        if (!f.type.match('image.*')){
            $('#list').append('<img class="file-thumb" src="/static/download168.png"/>');
            continue;
        }
        var reader = new FileReader();

        //Closure to capture file information
        reader.onload = (function(theFile){
                return function(e){
                    //Render thumbnail
                    $('#list').append('<img class="thumb" src="'+e.target.result+'" title="'+escape(theFile.name)+'"/>'); 
                    };
                    })(f);
        reader.readAsDataURL(f);
    }
    }

console.log("Url is",urls);`   

And my ajax function

    //Code for Image upload

// Custom jQuery xhr instance to support our progress bar.

var xhr_with_progress = function() {
     var xhr = new XMLHttpRequest();
     xhr.upload.addEventListener("progress",
         function(evt) {
             if (!evt.lengthComputable) return;
             var percentComplete = evt.loaded / evt.total;
             $("#progress-bar div.progress-bar").css('width', String(100*percentComplete) + "%");
         }, false);
     return xhr;
 };





$.support.cors = true;//For cross origin transfer

//Event listners to avoid default drag and drop reaction of browser
window.addEventListener("dragover",function(e){
  e = e || event;
  e.preventDefault();
},false);
window.addEventListener("drop",function(e){
  e = e || event;
  e.preventDefault();
},false);



function handleFiles(file,callback){

            var filename = file.name;
            $.ajax({
                type:'GET',
                data:{"filename":file.name, "FileType":"question_file"},
                url:'/dashboard/generateuploadurl/',
                contentType:"application/json",
                dataType:"json",
                async:false,
                success: function(data){ 
                    if(data.UploadUrl){
                      console.log("upload url successfully created for " + file.name + " file");
                        console.log(data.UploadUrl);
                        handleUpload(data.UploadUrl, file, data.Filename,callback);

                    }
                },
                error: function(data){ 
                    console.log("error occured while creating upload url for " + file.name + ' file');
                    console.log(data);
                },
            });
        }           
function handleUpload(UploadUrl, file, Filename,callback){
    $.ajax({
        xhr:xhr_with_progress,
        url:UploadUrl,
        type:'PUT',
        data:file,
        cache:false,
        contentType:false,
        processData:false,
        success: function(data){
            console.log('https://7c.ssl.cf6.rackcdn.com/'+ Filename);
            callback('https://7c.ssl.cf6.rackcdn.com/'+ Filename);
        },
        error: function(data){ 
            alert("error occured while uploading " + file.name );
            console.log(data);
        },
    }); 
}

Coordinating multiple asynchronous operations is a job best solved with tools like promises. So, in the long run, I'd suggest you read up about promises and how to use them.

Without promises, here's a brute force way you can tell when all your handleFiles() operations are done by using a counter to know when the last async operation is done and then using the urls variable INSIDE that last callback or calling a function from there and passing the urls variable to that function:

document.getElementById('question-file-selector').addEventListener('change', handleFileSelect, false);

function handleFileSelect(evt) {
    var files = evt.target.files; //File list object
    var urls = "";
    // Loop through file list, render image files as thumbnails
    var doneCnt = 0;
    for (var i = 0, f; f = files[i]; i++) {
        handleFiles(f, function (url) {
            urls = urls + url + ',';
            console.log("urls is " + urls);
            ++doneCnt;
            if (doneCnt === files.length) {
                // The last handleFiles async operation is now done
                // final value is in urls variable here
                // you can use it here and ONLY here

                // Note: this code here will get executed LONG after the
                // handleFileSelect() function has already finished executing
            }
        });
        // Only process image files
        if (!f.type.match('image.*')) {
            $('#list').append('<img class="file-thumb" src="/static/download168.png"/>');
            continue;
        }
        var reader = new FileReader();
        //Closure to capture file information
        reader.onload = (function (theFile) {
            return function (e) {
                //Render thumbnail
                $('#list').append('<img class="thumb" src="' + e.target.result + '" title="' + escape(theFile.name) + '"/>');
            };
        })(f);
        reader.readAsDataURL(f);
    }
}

// You can't use the urls variable here.  It won't be set yet.

Ohhh. Please remove the async: false from your ajax call. That's a horrible thing to code with for a variety of reasons.


Here's a version using the promises built into jQuery:

document.getElementById('question-file-selector').addEventListener('change', handleFileSelect, false);

function handleFileSelect(evt) {
    var files = evt.target.files; //File list object
    // Loop through file list, render image files as thumbnails
    var promises = [];
    for (var i = 0, f; f = files[i]; i++) {
        promises.push(handleFiles(f));
        // Only process image files
        if (!f.type.match('image.*')) {
            $('#list').append('<img class="file-thumb" src="/static/download168.png"/>');
            continue;
        }
        var reader = new FileReader();
        //Closure to capture file information
        reader.onload = (function (theFile) {
            return function (e) {
                //Render thumbnail
                $('#list').append('<img class="thumb" src="' + e.target.result + '" title="' + escape(theFile.name) + '"/>');
            };
        })(f);
        reader.readAsDataURL(f);
    }
    $.when.apply($, promises).then(function() {
        var results = Array.prototype.slice.call(arguments);
        // all handleFiles() operations are complete here
        // results array contains list of URLs (some could be empty if there were errors)
    });
}

function handleFiles(file) {
    var filename = file.name;
    return $.ajax({
        type: 'GET',
        data: {
            "filename": file.name,
            "FileType": "question_file"
        },
        url: '/dashboard/generateuploadurl/',
        contentType: "application/json",
        dataType: "json"
    }).then(function(data) {
        if (data.UploadUrl) {
            console.log("upload url successfully created for " + file.name + " file");
            console.log(data.UploadUrl);
            return handleUpload(data.UploadUrl, file, data.Filename);
        }
    }, function(err) {
        console.log("error occured while creating upload url for " + file.name + ' file');
        console.log(err);
        // allow operation to continue upon error
    });
}

function handleUpload(UploadUrl, file, Filename) {
    return $.ajax({
        xhr: xhr_with_progress,
        url: UploadUrl,
        type: 'PUT',
        data: file,
        cache: false,
        contentType: false,
        processData: false
    }).then(function(data) {
        console.log('https://7c.ssl.cf6.rackcdn.com/' + Filename);
        return 'https://7c.ssl.cf6.rackcdn.com/' + Filename;
    }, function(err) {
        console.log("error occured while uploading " + file.name);
        console.log(err);
        // allow operation to continue upon error
    });
}

Since I can't run this code to test it, there could be a mistake or two in here, but you should be able to debug those mistakes or tell us where the errors occur and we can help you debug it. These are conceptually how you solve these types of problems of coordinating multiple async operations.

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