Chapter 11

Async JavaScript

Learning Objectives

  • Understand asynchronous code
  • Work with Promises
  • Use async/await syntax
  • See how MixMind handles file loading and analysis

What is Async?

Some operations take time: loading files, network requests, analyzing audio. If we wait for each to finish before continuing, the page would freeze.

Asynchronous code lets JavaScript start a long operation and continue running other code while waiting.

// BAD - blocks everything
const buffer = loadAudioFile("song.mp3"); // Page freezes!
analyzeAudio(buffer); // Has to wait

// GOOD - doesn't block
loadAudioFile("song.mp3").then(buffer => {
    analyzeAudio(buffer);
});
console.log("This runs immediately!");

Promises

A Promise represents a value that will be available in the future. It can be:

  • Pending - Still working
  • Fulfilled - Completed successfully
  • Rejected - Failed
// Creating a Promise
const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        const success = true;
        if (success) {
            resolve("Data loaded!");
        } else {
            reject("Error occurred");
        }
    }, 2000);
});

// Using a Promise
promise
    .then(result => {
        console.log(result); // "Data loaded!"
    })
    .catch(error => {
        console.error(error);
    });

// Chaining Promises
loadFile("song.mp3")
    .then(buffer => decodeAudio(buffer))
    .then(audioData => analyzeAudio(audioData))
    .then(analysis => displayResults(analysis))
    .catch(error => console.error("Failed:", error));

Async/Await

async/await is modern syntax that makes async code look synchronous:

// Async function
async function processTrack(file) {
    try {
        // await pauses until Promise resolves
        const buffer = await loadFile(file);
        const audio = await decodeAudio(buffer);
        const analysis = await analyzeAudio(audio);
        
        return analysis;
    } catch (error) {
        console.error("Processing failed:", error);
        throw error;
    }
}

// Calling async function
processTrack("song.mp3")
    .then(result => console.log(result))
    .catch(error => console.error(error));

// In event handler
uploadBtn.addEventListener("click", async () => {
    const file = fileInput.files[0];
    const analysis = await processTrack(file);
    updateUI(analysis);
});

// Multiple async operations
async function processAllTracks(files) {
    const promises = files.map(file => processTrack(file));
    const results = await Promise.all(promises);
    return results;
}

🎵 MixMind Connection

How MixMind Uses Async

// Loading and analyzing audio
async function loadAndAnalyze(file) {
    // Read file
    const arrayBuffer = await file.arrayBuffer();
    
    // Decode audio
    const audioContext = new AudioContext();
    const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
    
    // Analyze (takes time)
    const bpm = await detectBPM(audioBuffer);
    const key = await detectKey(audioBuffer);
    const energy = await calculateEnergy(audioBuffer);
    
    return { file, audioBuffer, bpm, key, energy };
}

// Processing multiple files
async function handleUpload(files) {
    const results = [];
    for (const file of files) {
        updateProgress("Analyzing " + file.name);
        const result = await loadAndAnalyze(file);
        results.push(result);
    }
    return results;
}

// WebAssembly calls
async function separateStems(audioBuffer) {
    const wasmModule = await loadWasmModule();
    const result = await wasmModule.separate(audioBuffer);
    return result;
}

Exercise 11.1: Practice

Goal: Apply what you've learned in this chapter

Try the concepts from this chapter in your browser's console or a simple HTML file. Experiment with the code examples and modify them to see what happens.

📝 Quick Reference:

Review the key concepts from this chapter and make sure you understand each one before moving to the next chapter.