WebAssembly (WASM) för högpresterande appar · Lektion

Bygga en komplett WASM-applikation

Tillämpa alla kunskaper du har fått för att utforma och bygga en avancerad, högpresterande applikation som drivs av WebAssembly

Lektion 3 av 412 steg

Bygga en komplett WASM-applikation är en gratis lektion i WebAssembly (WASM) för högpresterande appar på CoddyKit. Detta är lektion 3 av 4. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för WebAssembly (WASM) för högpresterande appar, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i WebAssembly (WASM) för högpresterande appar innehåller totalt 4 lektioner.

Bygg en komplett WASM-app

Du har lärt dig WebAssemblys byggstenar. Nu ska vi sätta ihop allt! En ”komplett” WASM-applikation integrerar högpresterande WASM-kod med en responsiv JavaScript-värdmiljö och hanterar data, samtidighet och fel på ett effektivt sätt.

Vi ska utforska hur man strukturerar en sådan applikation genom att tillämpa det du har lärt dig.

Ritning för en WASM-applikation

En typisk högpresterande WASM-applikation består av flera lager. JavaScript-värdmiljön hanterar användargränssnittet och logik på hög nivå, medan WASM-moduler utför beräkningsintensiva uppgifter. För komplexa uppgifter kan Web Workers flytta WASM-körningen till separata trådar, så att användargränssnittet inte fryser. Denna lagerindelning ger både hög hastighet och god responsivitet.

  • JavaScript-värdmiljö: användargränssnitt, API-anrop och samordning av WASM.
  • WASM-modul(er): central högpresterande logik.
  • Web Workers: bakgrundskörning för icke-blockerande åtgärder.
  • Delat minne: effektivt datautbyte mellan trådar.

Konfigurera vårt Rust-WASM-projekt

För att bygga en komplett applikation måste vårt Rust-WASM-projekt konfigureras så att det kompileras effektivt och samverkar smidigt med JavaScript. Vi använder wasm-pack för att bygga vår Rust-kod till en WASM-modul och wasm-bindgen för att generera den JavaScript-bryggkod som behövs för sömlös interoperabilitet.

Högpresterande bildlogik

Vi kan ta en enkel bildbehandlingsuppgift som exempel: att konvertera en bild till gråskala. Den beräkningsintensiva delen kommer att ligga i vår Rust-WASM-modul. Vi skriver en funktion som tar en pekare till bilddata i WASM:s linjära minne och bearbetar datan direkt.

Prova att köra detta förenklade Rust-exempel:

pub extern "C" fn apply_grayscale(ptr: *mut u8, len: usize) {
    let slice = unsafe {
        assert!(!ptr.is_null());
        std::slice::from_raw_parts_mut(ptr, len)
    };

    // Image data is typically RGBA, 4 bytes per pixel
    for i in (0..len).step_by(4) {
        let r = slice[i] as u32;
        let g = slice[i + 1] as u32;
        let b = slice[i + 2] as u32;
        let gray = ((r + g + b) / 3) as u8;

        slice[i] = gray;
        slice[i + 1] = gray;
        slice[i + 2] = gray;
        // Alpha channel (slice[i+3]) remains unchanged
    }
}

// A minimal main function for runnable context.
// In WASM, `apply_grayscale` would be directly called by JS.
fn main() {
    let mut image_data = [
        255, 0, 0, 255,   // Red pixel
        0, 255, 0, 255    // Green pixel
    ];
    let ptr = image_data.as_mut_ptr();
    let len = image_data.len();
    apply_grayscale(ptr, len);
    println!("Processed first pixel R: {} G: {} B: {}", image_data[0], image_data[1], image_data[2]);
}

Från JS till WASM: indata för bilddata

För att skicka vår bilddata (till exempel från ett canvas-ImageData) till WASM-modulen måste JavaScript skriva den till WASM:s linjära minne. Det innebär att en pekare till ett minnesområde hämtas från WASM och att Uint8ClampedArray sedan kopieras dit. wasm-bindgen förenklar ofta detta, men direkt minnesåtkomst är central.

Prova att köra detta exempel:

// Helper to simulate WASM memory and function call
const wasmMemory = new WebAssembly.Memory({ initial: 1 }); // 1 page = 64KB
const wasmByteView = new Uint8ClampedArray(wasmMemory.buffer);

// Simulate the WASM function (simplified grayscale logic)
const mockApplyGrayscale = (ptr, len) => {
  for (let i = ptr; i < ptr + len; i += 4) {
    const r = wasmByteView[i];
    const g = wasmByteView[i + 1];
    const b = wasmByteView[i + 2];
    const gray = Math.floor((r + g + b) / 3);
    wasmByteView[i] = gray;
    wasmByteView[i + 1] = gray;
    wasmByteView[i + 2] = gray;
  }
};

// Our "wasmModule" for this example
const wasmModule = {
  apply_grayscale: mockApplyGrayscale,
  memory: wasmMemory
};

// Example imageData (RGBA, 2 pixels)
const originalImageData = new Uint8ClampedArray([
  200, 100, 50, 255, // Pixel 1: Orange-ish
  50, 150, 200, 255  // Pixel 2: Blue-ish
]);

// Copy original data to WASM memory (offset 0 for simplicity)
wasmByteView.set(originalImageData, 0);

console.log("Original data in WASM memory (first 8 bytes):");
console.log(Array.from(wasmByteView.slice(0, 8)));

// Call the "WASM" function
wasmModule.apply_grayscale(0, originalImageData.length);

console.log("Processed data in WASM memory (first 8 bytes):");
console.log(Array.from(wasmByteView.slice(0, 8)));

Från WASM till JS: bearbetad utdata

När WASM har bearbetat datan finns resultaten redan i dess linjära minne. JavaScript kan sedan läsa den ändrade datan direkt från bufferten i WebAssembly.Memory. Det undviker kostsam kopiering av data, särskilt för stora datamängder som bildbuffertar.

Prova att köra detta exempel:

// Continuing from the previous example where wasmByteView has processed data

// Helper to simulate WASM memory and function call
const wasmMemory = new WebAssembly.Memory({ initial: 1 }); // 1 page = 64KB
const wasmByteView = new Uint8ClampedArray(wasmMemory.buffer);

// Simulate the WASM function (simplified grayscale logic)
const mockApplyGrayscale = (ptr, len) => {
  for (let i = ptr; i < ptr + len; i += 4) {
    const r = wasmByteView[i];
    const g = wasmByteView[i + 1];
    const b = wasmByteView[i + 2];
    const gray = Math.floor((r + g + b) / 3);
    wasmByteView[i] = gray;
    wasmByteView[i + 1] = gray;
    wasmByteView[i + 2] = gray;
  }
};

// Our "wasmModule" for this example
const wasmModule = {
  apply_grayscale: mockApplyGrayscale,
  memory: wasmMemory
};

// Example imageData (RGBA, 2 pixels)
const originalImageData = new Uint8ClampedArray([
  200, 100, 50, 255, // Pixel 1: Orange-ish
  50, 150, 200, 255  // Pixel 2: Blue-ish
]);

// Copy original data to WASM memory (offset 0 for simplicity)
wasmByteView.set(originalImageData, 0);

// Call the "WASM" function
wasmModule.apply_grayscale(0, originalImageData.length);

// To get the processed image data back into a JS array:
const processedImageData = new Uint8ClampedArray(
  wasmByteView.slice(0, originalImageData.length)
);

console.log("Data read back into JS array (first 8 bytes):");
console.log(Array.from(processedImageData.slice(0, 8)));

Behåll ett responsivt användargränssnitt med workers

Vid tunga beräkningar, som bildbehandling, kan körning av WASM direkt i huvudtråden blockera användargränssnittet. Lösningen är att flytta dessa uppgifter till en Web Worker. Workern laddar WASM-modulen och utför beräkningen, och kommunicerar resultaten tillbaka till huvudtråden via meddelanden.

Detta exempel visar principen för meddelandeöverföring:

// main.js (simulated)
const worker = {
  onmessage: null,
  postMessage: (msg, transfers) => {
    console.log("Main thread sends to worker:", msg.type);
    // Simulate worker receiving and responding
    setTimeout(() => {
      if (msg.type === 'processImage') {
        const imageData = new Uint8ClampedArray(msg.data.buffer);
        // Simulate WASM processing
        for (let i = 0; i < imageData.length; i += 4) {
          const r = imageData[i];
          const g = imageData[i + 1];
          const b = imageData[i + 2];
          const gray = Math.floor((r + g + b) / 3);
          imageData[i] = gray;
          imageData[i + 1] = gray;
          imageData[i + 2] = gray;
        }
        if (worker.onmessage) {
          worker.onmessage({ data: { type: 'imageProcessed', result: imageData }, transfers: [imageData.buffer] });
        }
      }
    }, 10);
  }
};

worker.onmessage = (event) => {
  if (event.data.type === 'imageProcessed') {
    console.log("Main thread receives from worker:", event.data.type);
    console.log("Processed image data (first 8 bytes):", Array.from(event.data.result.slice(0, 8)));
    // Update UI with processed image data
  }
};

function sendImageToWorker(imageData) {
  // Transferrable objects improve performance for large data
  worker.postMessage({ type: 'processImage', data: imageData }, [imageData.buffer]);
}

// Example call (in main.js context, after image loaded)
const demoImageData = new Uint8ClampedArray([200,100,50,255, 50,150,200,255]);
console.log("Original demo image data (first 8 bytes):", Array.from(demoImageData.slice(0, 8)));
sendImageToWorker(demoImageData);

Delat minne för samtidighet

Web Workers förhindrar blockering av användargränssnittet, men överföring av stora datamängder mellan huvudtråden och workers kan fortfarande vara en flaskhals. SharedArrayBuffer gör det möjligt för båda trådarna att samtidigt komma åt samma minnesblock. Detta är avgörande för verkligt parallella WASM-beräkningar som behöver samordna eller dela tillstånd.

  • Ett minnesblock: tillgängligt för huvudtråden och alla workers.
  • Ingen kopiering: eliminerar kostnaden för dataöverföring.
  • Atomics: krävs för säker och synkroniserad åtkomst till delat minne.
  • Användningsområde: simuleringar i realtid och komplexa flertrådade algoritmer.

Hantera fel på ett smidigt sätt

I en komplett applikation är robust felhantering avgörande. När ett fel uppstår i WASM kraschar modulen vanligtvis. Vi behöver mekanismer för att fånga dessa fel och kommunicera dem tillbaka till JavaScript. Strategierna omfattar att returnera särskilda felkoder, exportera en WASM-funktion som anger ett feltillstånd eller använda den inbyggda felpropageringen i wasm-bindgen för Rusts Result-typer.

  • Returkoder: WASM-funktionen returnerar 0 vid lyckat resultat och ett värde som inte är noll vid fel.
  • Feltillstånd: WASM exporterar en funktion för att hämta det senaste felmeddelandet.
  • wasm-bindgen-fel: Rusts Result-typer kan automatiskt konverteras till JS-undantag.

Samordna hela arbetsflödet

Nu sätter vi ihop alla delar i vår gråskalningsapplikation. Föreställ dig en webbsida med en bild:

  1. Användaren laddar upp en bild.
  2. JavaScript läser in bilden i ett ImageData-objekt.
  3. JS skickar ImageData (eller den underliggande Uint8ClampedArray) till en Web Worker.
  4. Web Workern laddar WASM-modulen.
  5. Workern anropar WASM-funktionen för gråskala och skickar med pekaren till bilddatan.
  6. WASM bearbetar bilddatan i minnet.
  7. Workern tar emot den bearbetade datan (som redan finns i dess minnesvy).
  8. Workern skickar den bearbetade datan tillbaka till huvudtråden.
  9. Huvudtråden uppdaterar canvas-elementet med den nya ImageData.

Denna fullständiga cykel visar en högpresterande och icke-blockerande WASM-applikation.

Integrerade WASM-koncept

Föreställ dig en komplex WebAssembly-applikation som utför videobearbetning i realtid. Den använder Rust som kompilerats till WASM, samverkar med JavaScript och måste behålla ett responsivt användargränssnitt. Vilka av följande strategier är avgörande för att bygga en sådan applikation på ett effektivt sätt?

Bygga robusta WASM-applikationer

Du har nu sett hur olika WebAssembly-koncept kan kombineras för att bygga en avancerad applikation. Från att strukturera samspelet mellan JavaScript och WASM, till att hantera minne, använda Web Workers för samtidighet och säkerställa robust felhantering – dessa principer är centrala för att utveckla högpresterande WASM-lösningar som är redo för produktion.

WASM:s framtid handlar om sömlös integrering och om att frigöra nya nivåer av funktionalitet i webbapplikationer.

Gratis att börja

Lär dig WebAssembly (WASM) för högpresterande appar med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
12
Lektioner
48

Vanliga frågor

Är lektionen ”Bygga en komplett WASM-applikation” gratis?

Ja – hela texten till ”Bygga en komplett WASM-applikation” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i WebAssembly (WASM) för högpresterande appar, kan Ni uppgradera till CoddyKit PRO. Kursen i WebAssembly (WASM) för högpresterande appar innehåller totalt 4 lektioner.

Vad lär jag mig i ”Bygga en komplett WASM-applikation”?

Tillämpa alla kunskaper du har fått för att utforma och bygga en avancerad, högpresterande applikation som drivs av WebAssembly Ni övar på WebAssembly (WASM) för högpresterande appar med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig WebAssembly (WASM) för högpresterande appar?

Du behöver inga förkunskaper. Utbildningen i WebAssembly (WASM) för högpresterande appar på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 3 av 4.

Hur lång tid tar lektionen ”Bygga en komplett WASM-applikation”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här WebAssembly (WASM) för högpresterande appar-lektionen?

Ja. Varje WebAssembly (WASM) för högpresterande appar-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. WASM Component Model och framtida API:er
  2. Avancerade WASM-verktyg och ekosystem
  3. Bygga en komplett WASM-applikation
  4. WASM-skräpinsamling och referenstyper
← Tillbaka till WebAssembly (WASM) för högpresterande appar