Romain Billoir
Romain Billoir

Reputation: 13

Fix AJAX loading file twice issue WITHOUT jQuery

I'm developping a simple web application which needs to load 16 audio files to process an hearing test. But my code is loading the files twice!

The application needs to be very light and fast, so it's a big problem.

For the same reason, i don't want to use jQuery libraries.

function loadSound(array) {
    var i = 0;
    array.forEach(function(soundUrl) {
        var request = new XMLHttpRequest();
        request.open('GET', soundUrl, true);
        request.responseType = 'arraybuffer';

        request.onloadend = function() {
            var audioData = request.response;

            contextAudio.decodeAudioData(audioData, function(buffer) {
                sources[i] = contextAudio.createBufferSource();
                sources[i].buffer = buffer;
                sources[i].connect(contextAudio.destination);
                i++
            });
        };
        request.send(null);
    });
}

The soundList array and loadSound calling:

var soundList = new Array(
'http://localhost/testauditif/sons/440L.wav',
'http://localhost/testauditif/sons/440R.wav',
'http://localhost/testauditif/sons/125L.wav',
'http://localhost/testauditif/sons/125R.wav',
'http://localhost/testauditif/sons/250L.wav',
'http://localhost/testauditif/sons/250R.wav',
'http://localhost/testauditif/sons/500L.wav',
'http://localhost/testauditif/sons/500R.wav',
'http://localhost/testauditif/sons/1000L.wav',
'http://localhost/testauditif/sons/1000R.wav',
'http://localhost/testauditif/sons/2000L.wav',
'http://localhost/testauditif/sons/2000R.wav',
'http://localhost/testauditif/sons/4000L.wav',
'http://localhost/testauditif/sons/4000R.wav',
'http://localhost/testauditif/sons/8000L.wav',
'http://localhost/testauditif/sons/8000R.wav'
);

loadSound(soundList);

The javascript console: XHRGEThttp://localhost/testauditif/sons/440L.wav [HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/440R.wav
[HTTP/1.1 200 OK 0ms]

0 script.js:88:12
XHRGEThttp://localhost/testauditif/sons/125L.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/125R.wav
[HTTP/1.1 200 OK 0ms]

0 script.js:88:12
XHRGEThttp://localhost/testauditif/sons/250L.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/250R.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/500L.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/500R.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/1000L.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/1000R.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/2000L.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/2000R.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/4000L.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/4000R.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/8000L.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/8000R.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/440L.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/440R.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/125L.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/125R.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/250L.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/250R.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/500L.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/500R.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/1000L.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/1000R.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/2000L.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/2000R.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/4000L.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/4000R.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/8000L.wav
[HTTP/1.1 200 OK 0ms]

XHRGEThttp://localhost/testauditif/sons/8000R.wav
[HTTP/1.1 200 OK 0ms]

Upvotes: 1

Views: 80

Answers (2)

nightgaunt
nightgaunt

Reputation: 910

Closures!!!

Lets take a clos(ure)er look at what's happening with your code.

You start iterating over the array with each URL defined as soundUrl in array.forEach(function(soundUrl) {}). Please note that variable i is retained as it is inside this anonymous function because it is defined in the loadSound function.

You send your request using request.send(null);. In the request.onloadend function, the i value is 0. All good so far.

Now comes the trouble. Without waiting for onloadend to be called, you move to the next soundUrl in your forEach loop. When the request.onloadend is initialized, the i value is still 0 unless onloadend for previous request is already called. (This is highly unlikely assuming the audio files take a while to download to browser)

Somewhere in middle of your forEach loop, request.onloadend get called for your first request incrementing the i.

End result? You end up with a pile garbage for in sources array where few files are downloaded, few are overwritten by next audio files with gaping holes in between.

PS: As the code stands, it doesn't work. Is that the real issue? May be. Without further information about what the array contains and how many requests the code is sending to backend.


The solution I would suggest.

function loadSound(array) {
    array.forEach(function(soundUrl, i) {
        // No need to declare var i for iteration. Foreach provides the index.
        var request = new XMLHttpRequest();
        // Creating an instance of XMLHttpRequest inside loop to ensure 
        // request.onloadend does not get overriden in the next iteration.
        request.open('GET', soundUrl, true);
        request.responseType = 'arraybuffer';

        request.onloadend = function() {
            // Retain i inside the function using a local variable inside the callback function.
            var idx = i;
            var audioData = request.response;

            contextAudio.decodeAudioData(audioData, function(buffer) {
                // Not sure whether decodeAudioData in asyc. If so, you again need to retain idx inside the callback.
                var src_idx = idx;
                sources[src_idx] = contextAudio.createBufferSource();
                sources[src_idx].buffer = buffer;
                sources[src_idx].connect(contextAudio.destination);
            });
        };
        request.send(null);
    });
}

Upvotes: 1

2x2p
2x2p

Reputation: 444

Please try if this solves your problem

This version is not using var i for setting things in sources. If you need i later to know how many sources have been buffered. just make i the length of sources.

const request = new XMLHttpRequest();
var sources = null;
var i = null; // only if you want to keep i 

function loadSound(array) {
    console.log(array); // only here for testing
    var sources = []; // define or re-define sources as empty array

    array.forEach(function(soundUrl) {
        request.open('GET', soundUrl, true);
        request.responseType = 'arraybuffer';

        request.onloadend = function() {
            console.log(request); // only here for testing
            var audioData = request.response;

            contextAudio.decodeAudioData(audioData, function(buffer) {
                let newsource = contextAudio.createBufferSource(); 
                // let is ES6 style it can also work without let
                newsource.buffer = buffer;
                newsource.connect(contextAudio.destination);
                sources.push(newsource); 
                // added your new source to your sources array
                var i = sources.length; 
                // if you need var i elsewhere it now contains total number of sources
                // if you don't need var i hereafter remove it from the code
                console.log(sources); // only here for testing
            });
        };
        request.send(null);
    });
}

Upvotes: 0

Related Questions