Search code examples
javascriptarraysasync-awaites6-promiseweb-audio-api

Why array.length is not working while array is filled by a async function in javascript


I'm learing WebAudio API. I'm facing a problem with it. Basically things are asynchronous here...so im getting a bit confused. Please help.Here is my code:-

//"use strict";

var sources = new Array();
var actx;
var songs = ['src1.mp3', 'src2.mp3'];

async function start() {
  console.log("WELCOME!!");
  try {
    actx = new AudioContext();
  } catch (e) {
    console.log('WebAudio api is not supported!!');

  }
  await getBuffers(actx, songs);
  console.log(sources);
  console.log(sources.length);
}

function load_song(url) {
  let promise = new Promise((resolve, reject) => {
    let request = new XMLHttpRequest();
    request.open('GET', url, true);
    request.responseType = 'arraybuffer';
    request.onload = () => {
      let audioData = request.response;
      resolve(audioData);
    }
    request.onerror = () => {
      reject(new error("Could not load the song:- " + url));
    }

    request.send();
  });
  return promise;
}
//creats buffers
async function getBuffers(actx, songs) {
  // let buffer_list = new Array();

  for (let x = 0; x < songs.length; x++) {
    let temp = actx.createBufferSource();
    await load_song(songs[x]).then((audioData) => {
      actx.decodeAudioData(audioData).then((decodedAudioData) => {
        temp.buffer = decodedAudioData;
        sources.push(temp);
      }).catch((error) => {
        console.error(error);
      });
    });
  }
  //console.log(buffers.length);
}
async function play() {
  //start();
  sources[0].start(0);
  //sources[1].start(0);
}

function stop() {
  sources[0].stop(0);
  //sources[1].stop(0);
}

Here in the two lines console.log(sources) and console.log(sources.length). Here the results are. Why console.log(sources.length) is 0?
Please help me........Thank you.

in web colsole here is the result.


Solution

  • You need to return

    actx.decodeAudioData(audioData).then((decodedAudioData) => {
    

    As you are not returning it, you dont await it. Therefore the log appears before the array gets filled, however console.log(sources) is live, so you see the latest change.

    Such mistakes are more unlikely to happen when you use await everywhere, and thats also easier to read IMO:

    async function getBuffers(actx, songs) {
      const sources = [];  //mutating a global variable isnt good, so lets make it local
      for (const song of songs) { //for...of is so beautiful, why dont you use it?
       try { //this is similar to .catch
        let temp = actx.createBufferSource();
        const audioData = await load_song(song);
    
        const decodedAudioData =  await actx.decodeAudioData(audioData);
        temp.buffer = decodedAudioData;
        sources.push(temp);
       } catch(e){ console.error(e); } //okay that does not count as an error handler...
      }
      return sources; //getBuffers implies that it returns sth
    }