jQuery: Return data efter ajax-opkald succes

Jeg har noget som dette, hvor det er et simpelt kald til et script, der giver mig en værdi tilbage, en streng...

function testAjax() {
    $.ajax({
      url: "getvalue.php",  
      success: function(data) {
         return data; 
      }
   });
}

men hvis jeg kalder noget som dette

var output = testAjax(svar);  // output will be undefined...

så hvordan kan jeg returnere værdien? nedenstående kode ser heller ikke ud til at virke...

function testAjax() {
    $.ajax({
      url: "getvalue.php",  
      success: function(data) {

      }
   });
   return data; 
}

Bemærk: Dette svar blev skrevet i februar 2010.
Se opdateringer fra 2015, 2016 og 2017 nederst.
Du kan ikke returnere noget fra en funktion, der er asynkron. Det, du kan returnere, er et løfte. Jeg forklarede, hvordan promises fungerer i jQuery i mine svar på disse spørgsmål:

function testAjax() {
  $.ajax({
    url: "getvalue.php",  
    success: function(data) {
      return data; 
    }
  });
}

kan du skrive din testAjax-funktion sådan her:

function testAjax() {
  return $.ajax({
      url: "getvalue.php"
  });
}

Så kan du få dit løfte på denne måde:

var promise = testAjax();

Du kan gemme dit løfte, du kan sende det rundt, du kan bruge det som et argument i funktionsopkald og du kan returnere det fra funktioner, men når du endelig vil bruge dine data, der returneres af AJAX-opkaldet, skal du gøre det sådan her:

promise.success(function (data) {
  alert(data);
});

(Se opdateringer nedenfor for forenklet syntaks.) Hvis dine data er tilgængelige på dette tidspunkt, vil denne funktion blive påberåbt med det samme. Hvis den ikke er det, vil den blive påberåbt, så snart dataene er tilgængelige. Hele pointen med at gøre alt dette er, at dine data ikke er tilgængelige umiddelbart efter opkaldet til $.ajax, fordi det er asynkront. Promises er en dejlig abstraktion for funktioner til at sige: Jeg kan ikke returnere dataene til dig, fordi jeg ikke har dem endnu, og jeg vil ikke blokere og lade dig vente, så her er et løfte i stedet, så du kan bruge dem senere, eller bare give dem til en anden og være færdig med dem. Se denne [DEMO](http://jsfiddle.net/jW68r/). UPDATE (2015)

I øjeblikket (fra marts 2015) er jQuery Promises ikke kompatible med Promises/A+-specifikationen, hvilket betyder, at de muligvis ikke samarbejder særlig godt med andre Promises/A+-konforme implementeringer. Men jQuery Promises i den kommende version 3.x vil være kompatible med Promises/A+-specifikationen (tak til Benjamin Gruenbaum for at gøre opmærksom på det). I øjeblikket (fra maj 2015) er de stabile versioner af jQuery 1.x og 2.x.

Det, jeg forklarede ovenfor (i marts 2011), er en måde at bruge jQuery Deferred Objects til at gøre noget asynkront, som i synkron kode ville blive opnået ved at returnere en værdi. Men et synkront funktionskald kan gøre to ting - det kan enten returnere en værdi (hvis det kan) eller kaste en undtagelse (hvis det ikke kan returnere en værdi). Promises/A+ tager begge disse anvendelsestilfælde op på en måde, der er stort set lige så effektiv som undtagelseshåndtering i synkron kode. JQuery-versionen håndterer det tilsvarende med at returnere en værdi fint, men det tilsvarende med kompleks undtagelseshåndtering er noget problematisk. Især er hele pointen med undtagelseshåndtering i synkron kode ikke bare at give op med en pæn besked, men at forsøge at løse problemet og fortsætte eksekveringen, eller eventuelt at genkaste den samme eller en anden undtagelse, så andre dele af programmet kan håndtere den. I synkron kode har man en call stack. I asynkrone kald har man ikke det, og avanceret undtagelseshåndtering inden for dine promises som krævet i Promises/A+-specifikationen kan virkelig hjælpe dig med at skrive kode, der håndterer fejl og undtagelser på en meningsfuld måde, selv i komplekse brugssituationer. For forskelle mellem jQuery og andre implementeringer, og hvordan man konverterer jQuery-promiser til Promises/A+-kompatible, se Coming from jQuery af Kris Kowal et al. på Q-bibliotekets wiki og Promises arrive in JavaScript af Jake Archibald på HTML5 Rocks.

Sådan returnerer du et rigtigt løfte

Funktionen fra mit eksempel ovenfor:

function testAjax() {
  return $.ajax({
      url: "getvalue.php"
  });
}

returnerer et jqXHR-objekt, som er et jQuery Deferred Object. For at få den til at returnere et rigtigt løfte, kan du ændre det til - ved hjælp af metoden fra Q-wikien:

function testAjax() {
  return Q($.ajax({
      url: "getvalue.php"
  }));
}

eller ved hjælp af metoden fra HTML5 Rocks-artiklen:

function testAjax() {
  return Promise.resolve($.ajax({
      url: "getvalue.php"
  }));
}

Denne Promise.resolve($.ajax(...)) er også det, der er forklaret i promise-modulets dokumentation, og det burde fungere med ES6 Promise.resolve(). For at bruge ES6 Promises i dag kan du bruge es6-promise module's polyfill() af Jake Archibald. For at se hvor du kan bruge ES6 Promises uden polyfill, se: Can I use: Promises. For yderligere oplysninger se:

promise.success(function (data) {
  alert(data);
});

kan du skrive:

promise.success(data => alert(data));

eller ved hjælp af Promises/A+ API'et:

promise.then(data => alert(data));

Husk at du altid skal bruge afvisningshåndteringsprogrammer enten med:

promise.then(data => alert(data), error => alert(error));

eller med:

promise.then(data => alert(data)).catch(error => alert(error));

Se dette svar for at se, hvorfor du altid skal bruge afvisningshåndteringsprogrammer med løfter:

promise.then(data => alert("x is " + data.x));

Ikke alle browsere understøtter denne syntaks endnu, men der er visse tilfælde, hvor du er sikker på, hvilken browser din kode skal køre i - f.eks. når du skriver en Chrome-udvidelse, en Firefox-udvidelse eller en desktop-applikation ved hjælp af Electron, NW.js eller AppJS (se dette svar for nærmere oplysninger). For understøttelse af pilefunktioner, se:

  • Der er en endnu nyere syntaks lige nu kaldet async-funktioner med et nyt nøgleord await, der i stedet for denne kode:
functionReturningPromise()
    .then(data => console.log('Data:', data))
    .catch(error => console.log('Error:', error));

lader dig skrive:

try {
    let data = await functionReturningPromise();
    console.log('Data:', data);
} catch (error) {
    console.log('Error:', error);
}

Du kan kun bruge det inden for en funktion oprettet med nøgleordet async. For mere information, se:

Kommentarer (9)
Løsning

Den eneste måde at returnere dataene fra funktionen på ville være at foretage et synkront opkald i stedet for et asynkront opkald, men det ville fastfryse browseren, mens den venter på svaret.

Du kan indsætte en callback-funktion, der håndterer resultatet:

function testAjax(handleData) {
  $.ajax({
    url:"getvalue.php",  
    success:function(data) {
      handleData(data); 
    }
  });
}

Kald den sådan her:

testAjax(function(output){
  // here you use the output
});
// Note: the call won't wait for the result,
// so it will continue with the code here while waiting.
Kommentarer (7)

Se jquery docs eksempel: http://api.jquery.com/jQuery.ajax/ (ca. 2/3 af siden)

Du leder måske efter følgende kode:

    $.ajax({
     url: 'ajax/test.html',
     success: function(data) {
     $('.result').html(data);
     alert('Load was performed.');
   }
});

Samme side...længere nede.

Kommentarer (0)