35 Domanda: Come posso restituire la risposta da una chiamata asincrona?

domanda creata a Tue, Feb 26, 2019 12:00 AM

Ho una funzione foo che effettua una richiesta Ajax. Come posso restituire la risposta da foo?

Ho provato a restituire il valore dal callback success e ad assegnare la risposta a una variabile locale all'interno della funzione e a restituirlo, ma nessuno di questi modi restituisce effettivamente la risposta.

 
function foo() {
    var result;

    $.ajax({
        url: '...',
        success: function(response) {
            result = response;
            // return response; // <- I tried that one as well
        }
    });

    return result;
}

var result = foo(); // It always ends up being `undefined`.
    
4982
  1. Per una semplice intro di Promises, questa può essere una buona lettura: devopedia.org/promises
    2018-02-14 09: 20: 09Z
  2. [Stavo affrontando lo stesso problema delle chiamate asincrone usando axios e js, anche se non è consigliabile usare una funzione sincrona, funziona per certe situazioni. ] ( stackoverflow.com/questions/55239719/... )
    2019-04-10 14: 15: 35Z
  3. Non puoi farlo funzionare nel modo desiderato. anche se async /await funzionerebbe. di nuovo è basato sulla promessa
    2019-05-22 15: 04: 36Z
30 risposte                              30                         
  

→ Per una spiegazione più generale del comportamento asincrono con diversi esempi, vedi Perché la mia variabile è inalterata dopo averlo modificato all'interno di una funzione? - Riferimento codice asincrono

     

→ Se hai già capito il problema, passa alle possibili soluzioni di seguito.

Il problema

Il A in Ajax sta per asincrono . Ciò significa che l'invio della richiesta (o piuttosto la ricezione della risposta) viene portato fuori dal normale flusso di esecuzione. Nell'esempio, $.ajax restituisce immediatamente e l'istruzione successiva, return result;, viene eseguita prima che la funzione passata come chiamata success sia stata persino chiamata.

Ecco un'analogia che, auspicabilmente, fa la differenza tra il clearing sincrono e quello asincrono:

sincrono

Immagina di fare una telefonata ad un amico e chiedergli di cercare qualcosa per te. Anche se potrebbe volerci un po 'di tempo, aspetti al telefono e fissi nello spazio, finché il tuo amico non ti dà la risposta di cui avevi bisogno.

Lo stesso accade quando si effettua una chiamata di funzione che contiene il codice "normale":

 
function findItem() {
    var item;
    while(item_not_found) {
        // search
    }
    return item;
}

var item = findItem();

// Do something with item
doSomethingElse();

Anche se il findItem potrebbe impiegare molto tempo per essere eseguito, qualsiasi codice che viene dopo var item = findItem(); deve attendere finché la funzione non restituisce il risultato.

Asynchronous

Chiama di nuovo il tuo amico per lo stesso motivo. Ma questa volta gli dici che sei di fretta e lui dovrebbe richiamarti sul tuo cellulare. Riattacca, esci di casa e fai qualunque cosa tu abbia pianificato di fare. Una volta che il tuo amico ti ha richiamato, hai a che fare con le informazioni che ti ha dato.

Questo è esattamente ciò che accade quando esegui una richiesta Ajax.

 
findItem(function(item) {
    // Do something with item
});
doSomethingElse();

Invece di aspettare la risposta, l'esecuzione continua immediatamente e viene eseguita la dichiarazione dopo la chiamata Ajax. Per ottenere la risposta alla fine, si fornisce una funzione da chiamare una volta ricevuta la risposta, una richiamata ​​em> (notare qualcosa? richiamare ?). Qualsiasi istruzione che viene dopo quella chiamata viene eseguita prima che venga richiamata la chiamata.


Soluzione (s)

Abbraccia la natura asincrona di JavaScript! Mentre certe operazioni asincrone forniscono controparti sincrone (anche "Ajax"), è generalmente scoraggiato usarle, specialmente in un contesto di browser.

Perché è male chiedi?

JavaScript viene eseguito nel thread dell'interfaccia utente del browser e qualsiasi processo a esecuzione prolungata bloccherà l'interfaccia utente, rendendola unresponsive. Inoltre, esiste un limite superiore al tempo di esecuzione per JavaScript e il browser chiederà all'utente se continuare l'esecuzione o meno.

Tutto ciò è davvero un'esperienza negativa per l'utente. L'utente non sarà in grado di dire se tutto funziona correttamente o no. Inoltre, l'effetto sarà peggiore per gli utenti con una connessione lenta.

Nel seguito vedremo tre diverse soluzioni che si stanno costruendo l'una sull'altra:

  • Promesse con async/await (ES2017 +, disponibile nei browser più vecchi se si utilizza un transpiler o un rigeneratore)
  • Callback (popolare nel nodo)
  • Promesse con then() (ES2015 +, disponibile nei browser meno recenti se utilizzi una delle tante librerie promettenti)

Tutti e tre sono disponibili nei browser correnti e nel nodo 7 +.


ES2017 +: promette con async/await

La versione ECMAScript rilasciata nel 2017 ha introdotto supporto a livello di sintassi per le funzioni asincrone. Con l'aiuto di async e await, puoi scrivere in modo asincrono in uno "stile sincrono". Il codice è ancora asincrono, ma è più facile da leggere /capire.

async/await si basa su promesse: una funzione async restituisce sempre una promessa. await "estrae" una promessa e produce il valore con cui è stata risolta la promessa o genera un errore se la promessa è stata respinta.

Importante: puoi utilizzare solo await all'interno di una funzione async. Al momento, il livello superiore await non è ancora supportato, quindi potrebbe essere necessario creare un IIFE asincrono ( Immediatamente richiamato Funzione Espressione ) per avviare un contesto async.

Puoi leggere ulteriori informazioni su async e await su MDN.

Ecco un esempio che si basa sul ritardo sopra riportato:

 
// Using 'superagent' which will return a promise.
var superagent = require('superagent')

// This is isn't declared as `async` because it already returns a promise
function delay() {
  // `delay` returns a promise
  return new Promise(function(resolve, reject) {
    // Only `delay` is able to resolve or reject the promise
    setTimeout(function() {
      resolve(42); // After 3 seconds, resolve the promise with value 42
    }, 3000);
  });
}


async function getAllBooks() {
  try {
    // GET a list of book IDs of the current user
    var bookIDs = await superagent.get('/user/books');
    // wait for 3 seconds (just for the sake of this example)
    await delay();
    // GET information about each book
    return await superagent.get('/books/ids='+JSON.stringify(bookIDs));
  } catch(error) {
    // If any of the awaited promises was rejected, this catch block
    // would catch the rejection reason
    return null;
  }
}

// Start an IIFE to use `await` at the top level
(async function(){
  let books = await getAllBooks();
  console.log(books);
})();

Current browser e node supporta le versioni async/await. Puoi anche supportare ambienti più vecchi trasformando il tuo codice in ES5 con l'aiuto di rigeneratore (o strumenti che usano il rigeneratore , ad esempio Babel ).


Consenti alle funzioni di accettare callback

Un callback è semplicemente una funzione passata a un'altra funzione. Quell'altra funzione può chiamare la funzione passata ogni volta che è pronta. Nel contesto di un processo asincrono, il callback verrà chiamato ogni volta che viene eseguito il processo asincrono. Di solito, il risultato viene passato al callback.

Nell'esempio della domanda, puoi rendere foo accettare una richiamata e usarla come callback success. Quindi questo

 
var result = foo();
// Code that depends on 'result'

diventa ​​p>  

foo(function(result) {
    // Code that depends on 'result'
});

Qui abbiamo definito la funzione "in linea" ma puoi passare qualsiasi riferimento alla funzione:

 
function myCallback(result) {
    // Code that depends on 'result'
}

foo(myCallback);

foo stesso è definito come segue:

 
function foo(callback) {
    $.ajax({
        // ...
        success: callback
    });
}

callback si riferirà alla funzione che passiamo a foo quando la chiamiamo e semplicemente la passiamo a success. Cioè una volta che la richiesta Ajax ha avuto successo, $.ajax chiamerà callback e passerà la risposta al callback (a cui si può fare riferimento con result, poiché è così che abbiamo definito il callback).

Puoi anche elaborare la risposta prima di passarla alla richiamata:

 
function foo(callback) {
    $.ajax({
        // ...
        success: function(response) {
            // For example, filter the response
            callback(filtered_response);
        }
    });
}

È più facile scrivere codice utilizzando i callback di quanto possa sembrare. Dopotutto, JavaScript nel browser è pesantemente guidato dagli eventi (eventi DOM). Ricevere la risposta Ajax non è altro che un evento.
Potrebbero sorgere difficoltà quando si deve lavorare con il codice di terze parti, ma la maggior parte dei problemi può essere risolta semplicemente pensando al flusso dell'applicazione.


ES2015 +: promette con then ()

L' API Promise è una nuova funzione di ECMAScript 6 (ES2015), ma ha già un buon supporto browser . Ci sono anche molte librerie che implementano l'API Promises standard e forniscono metodi aggiuntivi per facilitare l'uso e la composizione di asincronifunzioni (ad es. bluebird ).

Le promesse sono contenitori per i valori futuri . Quando la promessa riceve il valore (è risolto ) o quando viene annullato ( rifiutato ), notifica a tutti i suoi "ascoltatori" che vogliono accedere a questo valore.

Il vantaggio rispetto ai callback semplici è che ti permettono di disaccoppiare il tuo codice e sono più facili da comporre.

Ecco un semplice esempio di utilizzo di una promessa:

 
function delay() {
  // `delay` returns a promise
  return new Promise(function(resolve, reject) {
    // Only `delay` is able to resolve or reject the promise
    setTimeout(function() {
      resolve(42); // After 3 seconds, resolve the promise with value 42
    }, 3000);
  });
}

delay()
  .then(function(v) { // `delay` returns a promise
    console.log(v); // Log the value once it is resolved
  })
  .catch(function(v) {
    // Or do something else if it is rejected 
    // (it would not happen in this example, since `reject` is not called).
  });

Applicato alla nostra chiamata Ajax potremmo usare promesse come questa:

 
function ajax(url) {
  return new Promise(function(resolve, reject) {
    var xhr = new XMLHttpRequest();
    xhr.onload = function() {
      resolve(this.responseText);
    };
    xhr.onerror = reject;
    xhr.open('GET', url);
    xhr.send();
  });
}

ajax("/echo/json")
  .then(function(result) {
    // Code depending on result
  })
  .catch(function() {
    // An error occurred
  });

Descrivere tutti i vantaggi che promettono l'offerta va oltre lo scopo di questa risposta, ma se scrivi un nuovo codice, dovresti prenderli seriamente in considerazione. Offrono una grande astrazione e separazione del codice.

Maggiori informazioni sulle promesse: rocce HTML5 - Promesse JavaScript

Nota a margine: oggetti posticipati di jQuery

Gli oggetti posticipati sono l'implementazione personalizzata delle promesse di jQuery (prima che l'API Promise fosse standardizzata). Si comportano quasi come delle promesse ma espongono un'API leggermente diversa.

Ogni metodo Ajax di jQuery restituisce già un "oggetto posticipato" (in realtà una promessa di un oggetto posticipato) che puoi semplicemente restituire dalla tua funzione:

 
function ajax() {
    return $.ajax(...);
}

ajax().done(function(result) {
    // Code depending on result
}).fail(function() {
    // An error occurred
});

Nota a margine: promuovi trucchi

Ricorda che le promesse e gli oggetti posticipati sono solo contenitori per un valore futuro, non sono il valore stesso. Ad esempio, supponi di avere il seguente:

 
function checkPassword() {
    return $.ajax({
        url: '/password',
        data: {
            username: $('#username').val(),
            password: $('#password').val()
        },
        type: 'POST',
        dataType: 'json'
    });
}

if (checkPassword()) {
    // Tell the user they're logged in
}

Questo codice fraintende i suddetti problemi di asincronia. In particolare, $.ajax() non blocca il codice mentre controlla la pagina '/password' sul server - invia una richiesta al server e, mentre attende, restituisce immediatamente un oggetto rinviato Ajax jQuery, non la risposta dal server. Ciò significa che l'istruzione if otterrà sempre questo oggetto rinviato, lo tratterà come true e procederà come se l'utente abbia effettuato l'accesso. Non va bene.

Ma la soluzione è semplice:

 
checkPassword()
.done(function(r) {
    if (r) {
        // Tell the user they're logged in
    } else {
        // Tell the user their password was bad
    }
})
.fail(function(x) {
    // Tell the user something bad happened
});

Non consigliato: chiamate "Ajax" sincrone

Come ho già detto, alcune (!) operazioni asincrone hanno controparti sincrone. Non sostengo il loro uso, ma per completezza, ecco come eseguire una chiamata sincrona:

Senza jQuery

Se usi direttamente un XMLHTTPRequest oggetto, passa false come terzo argomento a .open .

jQuery

Se usi jQuery , puoi impostare l'opzione async su false. Nota che questa opzione è deprecata ​​em> da quando jQuery 1.8. È quindi possibile continuare a utilizzare una callback success o accedere alla proprietà responseText dell'oggetto jqXHR :

 
function foo() {
    var jqXHR = $.ajax({
        //...
        async: false
    });
    return jqXHR.responseText;
}

Se usi altri metodi jQuery Ajax, come $.get, $.getJSON, ecc., devi cambiarlo in $.ajax (dato che puoi passare solo i parametri di configurazione a $.ajax).

Alza le teste! Non è possibile creare un JSONP sincrono. richiesta. JSONP per sua stessa natura è sempre asincrono (un motivo in più per non considerare nemmeno questa opzione).

    
5282
2019-03-03 17: 28: 08Z
  1. @ Pommy: se vuoi usare jQuery, devi includerlo. Fai riferimento a docs.jquery.com/Tutorials:Getting_Started_with_jQuery .
    2013-01-17 10: 47: 54Z
  2. Nella soluzione 1, sub jQuery, non ho capito questa linea: If you use any other jQuery AJAX method, such as $.get, $.getJSON, etc., you have them to $.ajax. (Sì, mi rendo conto che il mio nick è un po 'ironico in questo caso)
    2013-02-06 21: 07: 03Z
  3. @ gibberish: Mmmh, non so come possa essere reso più chiaro. Vedi come viene chiamato foo e gli viene passata una funzione (foo(function(result) {....});)? result è utilizzato all'interno di questa funzione ed è la risposta della richiesta Ajax. Per fare riferimento a questa funzione, il primo parametro di foo è chiamato callback e assegnato a success anziché an funzione anonima. Quindi, $.ajax chiamerà callback quando la richiesta ha avuto successo. Ho provato a spiegarlo un po 'di più.
    2013-02-06 23: 29: 52Z
  4. La chat per questa domanda è morta quindi non sono sicuro di dove proporre le modifiche delineate, ma propongo: 1) Modificare la parte sincrona in una semplice discussione del perché è cattivo senza esempi di codice su come farlo. 2) Rimuovi /unisci gli esempi di callback per mostrare solo l'approccio Deferred più flessibile, che a mio avviso potrebbe anche essere un po 'più facile da seguire per coloro che imparano Javascript.
    2013-04-16 02: 45: 22Z
  5. @ Jessi: Penso che tu abbia frainteso quella parte della risposta. Non è possibile utilizzare $.getJSON se si desidera che la richiesta Ajax sia sincrona. Tuttavia, non è necessario che l'evento desideri che la richiesta sia sincrona, in modo che non si applichi. Dovresti utilizzare callback o promesse per gestire la risposta, come spiegato in precedenza nella risposta.
    2015-10-08 17: 44: 18Z

Se non utilizza jQuery nel tuo codice, questa risposta è per te

Il tuo codice dovrebbe essere qualcosa del genere:

 
function foo() {
    var httpRequest = new XMLHttpRequest();
    httpRequest.open('GET', "/echo/json");
    httpRequest.send();
    return httpRequest.responseText;
}

var result = foo(); // always ends up being 'undefined'

Felix Kling ha fatto un ottimo lavoro scrivendo una risposta per le persone che usano jQuery per AJAX, ho deciso di fornire un'alternativa per le persone che non lo sono.

( Nota, per coloro che utilizzano la nuova API fetch, Angular o promesse che ho aggiunto un'altra risposta di seguito )


Cosa stai affrontando

Questo è un breve riassunto di "Spiegazione del problema" dall'altra risposta, se non sei sicuro dopo aver letto questo, leggi quello.

Il A in AJAX sta per asincrono . Ciò significa che l'invio della richiesta (o piuttosto la ricezione della risposta) viene portato fuori dal normale flusso di esecuzione. Nel tuo esempio, .send restituisce immediatamente e la prossima affermazione, return result;, viene eseguita prima della funzione che hai passato quando è stato chiamato anche il callback success.

Questo significa che quando stai tornando, l'ascoltatore che hai definito non è stato ancora eseguito, il che significa che il valore che stai restituendo non è stato definito.

Ecco una semplice analogia

 
function getFive(){ 
    var a;
    setTimeout(function(){
         a=5;
    },10);
    return a;
}

(Fiddle)

Il valore di a restituito è undefined poiché la parte a=5 non è stata ancora eseguita. AJAX agisce in questo modo, restituisci il valore prima che il server abbia la possibilità di dire al tuo browser quale sia quel valore.

Una possibile soluzione a questo problema è codificare re-attivamente , dicendo al tuo programma cosa fare quando il calcolo sarà completato.

 
function onComplete(a){ // When the code completes, do this
    alert(a);
}

function getFive(whenDone){ 
    var a;
    setTimeout(function(){
         a=5;
         whenDone(a);
    },10);
}

Questo è chiamato CPS . Fondamentalmente, stiamo trasmettendo getFive un'azione da eseguire al termine, stiamo dicendo al nostro codice come reagire quando un evento si completa (come la nostra chiamata AJAX, o in questo caso il timeout).

L'utilizzo sarebbe:

 
getFive(onComplete);

Che dovrebbe avvisare "5" sullo schermo. (Fiddle) .

Possibili soluzioni

Ci sono fondamentalmente due modi per risolvere questo problema:

  1. Rendi sincrona la chiamata AJAX (chiamala SJAX).
  2. Ristruttura il tuo codice per funzionare correttamente con i callback.

1. Synchronous AJAX - Non farlo !!

Come per AJAX sincrono, non farlo! la risposta di Felix solleva alcuni argomenti convincenti sul perché è una cattiva idea. Per riassumere, bloccherà il browser dell'utente fino a quando il server non restituirà la risposta e creerà un'esperienza utente molto negativa. Ecco un altro breve riassunto da MDN sul perché:

  

XMLHttpRequest supporta sia le comunicazioni sincrone che asincrone. In generale, tuttavia, le richieste asincrone dovrebbero essere preferite alle richieste sincrone per motivi di prestazioni.

     

In breve, le richieste sincrone bloccano l'esecuzione del codice ... ... questo può causare seri problemi ...

Se hai per farlo, puoi passare un flag: Ecco come:

 
var request = new XMLHttpRequest();
request.open('GET', 'yourURL', false);  // `false` makes the request synchronous
request.send(null);

if (request.status === 200) {// That's HTTP for 'ok'
  console.log(request.responseText);
}

2. Codice Ristruttura

Lascia la tua funzioneaccetta una richiamata. Nell'esempio il codice foo può essere fatto per accettare un callback. Diremo il nostro codice su come reagire al termine del foo.

 
var result = foo();
// code that depends on `result` goes here

diventa:

 
foo(function(result) {
    // code that depends on `result`
});

Qui abbiamo passato una funzione anonima, ma potremmo semplicemente passare un riferimento a una funzione esistente, facendolo apparire come:

 
function myHandler(result) {
    // code that depends on `result`
}
foo(myHandler);

Per ulteriori dettagli su come è fatto questo tipo di design callback, controlla la risposta di Felix.

Ora, definiamo foo stesso ad agire di conseguenza

 
function foo(callback) {
    var httpRequest = new XMLHttpRequest();
    httpRequest.onload = function(){ // when the request is loaded
       callback(httpRequest.responseText);// we're calling our method
    };
    httpRequest.open('GET', "/echo/json");
    httpRequest.send();
}

(violino)

Ora abbiamo fatto in modo che la nostra funzione foo accettasse un'azione da eseguire quando AJAX si completava correttamente, possiamo estendere ulteriormente controllando se lo stato della risposta non è 200 e agendo di conseguenza (creare un gestore errori e così via). Risolvendo efficacemente il nostro problema.

Se hai ancora difficoltà a comprendere questo leggi l'avvio di AJAX guida su MDN.

    
1017
2018-03-01 01: 03: 52Z
  1. "le richieste sincrone bloccano l'esecuzione del codice e possono perdere memoria ed eventi" come può una memoria di perdita di richiesta sincrona?
    2013-08-16 05: 54: 22Z
  2. @ MatthewG Ho aggiunto una taglia su di essa in questa domanda , vedrò cosa posso pescare. Sto rimuovendo la citazione dalla risposta nel frattempo.
    2013-08-16 08: 28: 00Z
  3. Solo per il riferimento, XHR 2 ci permette di usare il gestore onload, che si attiva solo quando readyState è 4. Certo, non è supportato in IE8. (Iirc, potrebbe aver bisogno di conferma.)
    2013-12-22 21: 09: 19Z
  4. La tua spiegazione su come passare una funzione anonima come callback è valida ma fuorviante. L'esempio var bar = pippo (); chiede una variabile da definire, mentre il tuo suggerito foo (functim () {}); non definisce la barra
    2014-08-07 10: 14: 22Z
  5. @ BenjaminGruenbaum Come posso restituire il contenuto di result in una variabile che posso usare da qualche altra parte nel mio codice piuttosto che stamparlo con html?
    2016-11-08 15: 39: 44Z

XMLHttpRequest 2 (prima di tutto leggi le risposte di Benjamin Gruenbaum & Felix Kling )

Se non usi jQuery e vuoi un bel XMLHttpRequest 2 che funzioni sui browser moderni e anche sui browser mobili ti consiglio di usarlo in questo modo:

 
function ajax(a, b, c){ // URL, callback, just a placeholder
  c = new XMLHttpRequest;
  c.open('GET', a);
  c.onload = b;
  c.send()
}

Come puoi vedere:

  1. È più breve di tutte le altre funzioni elencate.
  2. La richiamata è impostata direttamente (quindi non occorrono ulteriori chiusure inutili).
  3. Usa il nuovo onload (quindi non devi controllare lo stato di readystate &)
  4. Ci sono altre situazioni che non ricordo che rendono la XMLHttpRequest 1 fastidiosa.

Ci sono due modi per ottenere la risposta di questa chiamata Ajax (tre utilizzando il nome var XMLHttpRequest):

Il più semplice:

 
this.response

O se per qualche motivo hai bind() il callback a una classe:

 
e.target.response

Esempio:

 
function callback(e){
  console.log(this.response);
}
ajax('URL', callback);

O (quello sopra è meglio le funzioni anonime sono sempre un problema):

 
ajax('URL', function(e){console.log(this.response)});

Niente di più facile.

Ora alcune persone probabilmente diranno che è meglio usare onreadystatechange o anche il nome della variabile XMLHttpRequest. È sbagliato.

Controlla Funzioni avanzate XMLHttpRequest

Ha supportato tutti i * browser moderni. E posso confermare come sto usando questo approccio poiché XMLHttpRequest 2 esiste. Non ho mai avuto alcun tipo di problema su tutti i browser che uso.

sureadystatechange è utile solo se vuoi ottenere le intestazioni nello stato 2.

L'uso del nome variabile XMLHttpRequest è un altro grosso errore in quanto è necessario eseguire la richiamata all'interno delle chiusure onload /oreadystatechange che hai perso.


Ora se vuoi qualcosa di più complesso usando Post e FormData puoi facilmente estendere questa funzione:

 
function x(a, b, e, d, c){ // URL, callback, method, formdata or {key:val},placeholder
  c = new XMLHttpRequest;
  c.open(e||'get', a);
  c.onload = b;
  c.send(d||null)
}

Ancora ... è una funzione molto breve, ma ottiene & palo.

Esempi di utilizzo:

 
x(url, callback); // By default it's get so no need to set
x(url, callback, 'post', {'key': 'val'}); // No need to set post data

Oppure passa un elemento a modulo completo (document.getElementsByTagName('form')[0]):

 
var fd = new FormData(form);
x(url, callback, 'post', fd);

O imposta alcuni valori personalizzati:

 
var fd = new FormData();
fd.append('key', 'val')
x(url, callback, 'post', fd);

Come puoi vedere non ho implementato la sincronizzazione ... è una brutta cosa.

Detto questo ... perché non farlo nel modo più semplice?


Come menzionato nel commento l'uso dell'errore & sincrono interrompe completamente il punto della risposta. Quale è un bel modo breve per usare Ajax nel modo giusto?

Gestore errori

 
function x(a, b, e, d, c){ // URL, callback, method, formdata or {key:val}, placeholder
  c = new XMLHttpRequest;
  c.open(e||'get', a);
  c.onload = b;
  c.onerror = error;
  c.send(d||null)
}

function error(e){
  console.log('--Error--', this.type);
  console.log('this: ', this);
  console.log('Event: ', e)
}
function displayAjax(e){
  console.log(e, this);
}
x('WRONGURL', displayAjax);

Nello script precedente, hai un gestore di errori che è definito staticamente in modo da non compromettere la funzione. Il gestore degli errori può essere utilizzato anche per altre funzioni.

Ma per ottenere veramente un errore, il solo modo è scrivere un URL sbagliato, nel qual caso ogni browser genera un errore.

I gestori di errori sono forse utili se si impostano le intestazioni personalizzate, si imposta responseType sul buffer dell'array blob o qualsiasi altra cosa ...

Anche se si passa "POSTAPAPAP" come metodo, non verrà generato un errore.

Anche se passi 'fdggdgilfdghfldj' come formdata non genererà un errore.

Nel primo caso l'errore è all'interno del displayAjax() sotto il this.statusText come Method not Allowed.

Nel secondo caso, funziona semplicemente. Devi controllare sul lato server se hai passato i dati del post giusto.

Il dominio interregionale non consentito genera automaticamente l'errore.

Nella risposta all'errore, non ci sono codici di errore.

C'è solo il this.type che è impostato sull'errore.

Perché aggiungere un gestore di errori se non hai alcun controllo sugli errori? La maggior parte degli errori viene restituita all'interno di questo nella funzione di callback displayAjax().

Quindi: non c'è bisogno di verifiche degli errori se riesci a copiare e incollare correttamente l'URL. ;)

PS: Come primo test ho scritto x ('x', displayAjax) ..., e ha ottenuto una risposta totale ... ??? Quindi ho controllato la cartella in cui si trova l'HTML e c'era un file chiamato 'x.xml'. Quindi, anche se dimentichi l'estensione del tuo file XMLHttpRequest 2 TROVERAI . I LOL'd


Leggi un file sincrono

Non farlo.

Se vuoi bloccare il browser per un po 'di tempo carica un bel file .txt sincrono.

 
function omg(a, c){ // URL
  c = new XMLHttpRequest;
  c.open('GET', a, true);
  c.send();
  return c; // Or c.response
}

Ora puoi fare

 
 var res = omg('thisIsGonnaBlockThePage.txt');

Non c'è altro modo per farlo in modo non asincrono. (Sì, con il ciclo setTimeout ... ma sul serio?)

Un altro punto è ... se lavori con le API o solo i file della tua lista o qualsiasi altra cosa usi sempre funzioni diverse per ogni richiesta ...

Solo se hai una pagina in cui carichi sempre lo stesso XML /JSON o qualsiasi altra cosa hai bisogno di una sola funzione. In tal caso, modifica leggermente la funzione Ajax e sostituisci b con la tua funzione speciale.


Le funzioni sopra riportate sono per uso di base.

Se vuoi ESTENDERE la funzione ...

Sì, puoi.

Sto usando molte API e una delle prime funzioni che integro in ogni pagina HTML è la prima funzione Ajax in questa risposta, con GET solo ...

Ma puoi fare un sacco di cose con XMLHttpRequest 2:

Ho creato un gestore di download (utilizzando intervalli su entrambi i lati con curriculum, file manager, file system), vari convertitori di immagini con resizer utilizzando canvas, popola database SQL web con base64images e molto altro ... Ma in questi casi dovresti creare una funzione solo a tale scopo ... a volte hai bisogno di un blob, di buffer di array, puoi impostare le intestazioni, sovrascrivere il mimetype e c'è molto altro ...

Ma la domanda qui è come restituire una risposta Ajax ... (Ho aggiunto un modo semplice.)

    
372
2018-12-10 09: 08: 05Z
  1. Anche se questa risposta è carina (e tutti noi love XHR2 e postare dati di file e dati multipart è assolutamente fantastica) - questo mostra zucchero sintattico per postando XHR con JavaScript - potresti metterlo in un post del blog (mi piacerebbe) o anche in una libreria (non sono sicuro del nome x, ajax o xhr potrebbe essere più bello :)). Non vedo come indirizzi restituire la risposta da una chiamata AJAX. (qualcuno potrebbe ancora fare var res = x("url") e non capire perché non funziona;)). Una nota a margine: sarebbe bello se restituissi c dal metodo in modo che gli utenti possano collegarsi a error ecc.
    2013-08-23 05: 56: 49Z
  2. 2.ajax is meant to be async.. so NO var res=x('url').. Ecco l'intero punto di questa domanda e risposta:)
    2013-08-23 17: 28: 35Z
  3. perché c'è un parametro 'c' nelle funzioni, se sulla prima riga stai sovrascrivendo il valore che aveva? mi manca qualcosa?
    21-12-2016 10: 00: 52Z
  4. Puoi usare i parametri come segnaposto per evitare di scrivere più volte "var"
    21-12-2016 11: 05: 21Z
  5. @ cocco Quindi hai scritto codice ingannevole e illeggibile in una risposta SO per risparmiare qualche battuta? Per favore, non farlo.
    2017-10-08 06: 20: 55Z

Se usi le promesse, questa risposta è per te.

Significa AngularJS, jQuery (con differimento), sostituzione XHR nativa (recupero), EmberJS, salvataggio di BackboneJS o qualsiasi libreria di nodi che restituisce promesse.

Il tuo codice dovrebbe essere qualcosa del genere:

 
function foo() {
    var data;
    // or $.get(...).then, or request(...).then, or query(...).then
    fetch("/echo/json").then(function(response){
        data = response.json();
    });
    return data;
}

var result = foo(); // result is always undefined no matter what.

Felix Kling ha fatto un ottimo lavoro scrivendo una risposta per le persone che usano jQuery con i callback per AJAX. Ho una risposta per XHR nativo. Questa risposta è per l'uso generico di promesse sul frontend o back-end.


Il problema principale

Il modello di concorrenza JavaScript nel browser e sul server con NodeJS /io.js è asincrono e reattivo .

Ogni volta che chiami un metodo che restituisce una promessa, i gestori then sono sempre eseguiti in modo asincrono - cioè, dopo il codice sotto di loro che non è in un gestore .then .

Questo significa che quando si restituisce data il gestore then che hai definito non è stato ancora eseguito. Ciò a sua volta significa che il valore che stai restituendo non è stato impostato sul valore corretto nel tempo.

Ecco una semplice analogia per il problema:

 
    function getFive(){
        var data;
        setTimeout(function(){ // set a timer for one second in the future
           data = 5; // after a second, do this
        }, 1000);
        return data;
    }
    document.body.innerHTML = getFive(); // `undefined` here and not 5

Il valore di data è undefined poiché la parte data = 5 non è stata ancora eseguita. Probabilmente verrà eseguito in un secondo, ma a quel punto è irrilevante per il valore restituito.

Poiché l'operazione non è ancora avvenuta (AJAX, chiamata server, IO, timer) si restituisce il valore prima che la richiesta abbia la possibilità di dire al proprio codice quale sia quel valore.

Una possibile soluzione a questo problema è codificare re-attivamente , dicendo al tuo programma cosa fare quando il calcolo sarà completato. Le promesse lo rendono attivo attivamente essendo di natura temporale (sensibile al tempo).

Riepilogo delle promesse

Una promessa è un valore nel tempo . Le promesse hanno lo stato, iniziano come in sospeso senza valore e possono accontentarsi di:

  • soddisfatto che indica che il calcolo è stato completato con successo.
  • respinto significa che il calcolo non è riuscito.

Una promessa può cambiare solo gli stati una volta ​​em> dopo i quali rimarrà sempre nello stesso stato per sempre. È possibile collegare i gestori then alle promesse per estrarne il valore e gestire gli errori. I gestori then consentono di concatenare di chiamate. Le promesse sono create da utilizzando le API che li restituiscono . Ad esempio, la più moderna sostituzione AJAX fetch o il $.get ritorno di jQuery promette.

Quando chiamiamo .then su una promessa e restituiamo qualcosa da esso - otteniamo una promessa per il valore elaborato . Se restituiremo un'altra promessa otterremo cose incredibili, ma teniamo i nostri cavalli.

Con promesse

Vediamo come possiamo risolvere il problema di cui sopra con promesse. Per prima cosa, dimostriamo la nostra comprensione degli stati di promessa dall'alto usando Promise constructor per la creazione di una funzione di ritardo:

 
function delay(ms){ // takes amount of milliseconds
    // returns a new promise
    return new Promise(function(resolve, reject){
        setTimeout(function(){ // when the time is up
            resolve(); // change the promise to the fulfilled state
        }, ms);
    });
}

Ora, dopo aver convertito setTimeout per usare le promesse, possiamo usare then per renderlo valido:

 
function delay(ms){ // takes amount of milliseconds
  // returns a new promise
  return new Promise(function(resolve, reject){
    setTimeout(function(){ // when the time is up
      resolve(); // change the promise to the fulfilled state
    }, ms);
  });
}

function getFive(){
  // we're RETURNING the promise, remember, a promise is a wrapper over our value
  return delay(100).then(function(){ // when the promise is ready
      return 5; // return the value 5, promises are all about return values
  })
}
// we _have_ to wrap it like this in the call site, we can't access the plain value
getFive().then(function(five){ 
   document.body.innerHTML = five;
});

Fondamentalmente, invece di restituire un valore che non possiamo fare a causa del modello di concorrenza - stiamo restituendo un wrapper per un valore che possiamo > unwrap con then. È come una scatola che puoi aprire con then.

Applicazione di questo

Questo vale per la tua chiamata API originale, puoi:

 
function foo() {
    // RETURN the promise
    return fetch("/echo/json").then(function(response){
        return response.json(); // process it inside the `then`
    });
}

foo().then(function(response){
    // access the value inside the `then`
})

Quindi funziona altrettanto bene. Abbiamo appreso che non possiamo restituire valori da chiamate già asincrone, ma possiamo usare le promesse e concatenarle per eseguire l'elaborazione. Ora sappiamo come restituire la risposta da una chiamata asincrona.

ES2015 (ES6)

ES6 introduce generatori che sono funzioni che possono tornare nel mezzo e quindi riprendere il punto in cui erano. Questo è in genere utile per le sequenze, ad esempio:

 
function* foo(){ // notice the star, this is ES6 so new browsers/node/io only
    yield 1;
    yield 2;
    while(true) yield 3;
}

È una funzione che restituisce un iteratore sulla sequenza 1,2,3,3,3,3,.... che può essere iterata. Anche se questo è interessante da solo e apre spazio per molte possibilità, c'è un caso particolare interessante.

Se la sequenza che stiamo producendo è una sequenza di azioni piuttosto che numeri - possiamo sospendere la funzione ogni volta che viene prodotta un'azione e attendere che riprendiamo la funzione. Quindi, invece di una sequenza di numeri, abbiamo bisogno di una sequenza di valori futuri - ovvero: promesse.

Questo trucco un po 'complicato ma molto potente ci consente di scrivere codice asincrono in modo sincrono. Ci sono diversi "corridori" che fanno questo per te, scrivendo uno è un breve poche righe di codice ma è oltre lo scopo di questa risposta. Userò Bluebird's Promise.coroutine qui, ma ci sono altri wrapper come co o Q.async.

 
var foo = coroutine(function*(){
    var data = yield fetch("/echo/json"); // notice the yield
    // code here only executes _after_ the request is done
    return data.json(); // data is defined
});

Questo metodo restituisce una promessa, che possiamo consumare da altre coroutine. Ad esempio:

 
var main = coroutine(function*(){
   var bar = yield foo(); // wait our earlier coroutine, it returns a promise
   // server call done here, code below executes when done
   var baz = yield fetch("/api/users/"+bar.userid); // depends on foo's result
   console.log(baz); // runs after both requests done
});
main();

ES2016 (ES7)

In ES7, questo è ulteriormente standardizzato, ci sono diverse proposte in questo momento, ma in tutte è possibile promettere await. Questo è solo "zucchero" (sintassi più gradevole) per la proposta ES6 di cui sopra aggiungendo le parole chiave async e await. Eseguendo l'esempio precedente:

 
async function foo(){
    var data = await fetch("/echo/json"); // notice the await
    // code here only executes _after_ the request is done
    return data.json(); // data is defined
}

Restituisce comunque una promessa ugualmente:)

    
296
2017-05-23 12: 34: 51Z

Stai utilizzando Ajax in modo errato. L'idea è di non restituire nulla, ma invece di trasferire i dati a qualcosa chiamata funzione di callback, che gestisce i dati.

Cioè:

 
function handleData( responseData ) {

    // Do what you want with the data
    console.log(responseData);
}

$.ajax({
    url: "hi.php",
    ...
    success: function ( data, status, XHR ) {
        handleData(data);
    }
});

Restituire qualcosa nel gestore di invio non farà nulla. Devi invece consegnare i dati o fare ciò che vuoi direttamente all'interno della funzione di successo.

    
234
2015-11-21 14: 07: 03Z
  1. Questa risposta è completamente semantica ... il tuo metodo di successo è solo un callback all'interno di un callback. Potresti avere success: handleData e funzionerebbe.
    2016-01-04 15: 49: 49Z
  2. E se si desidera restituire "responseData" al di fuori di "handleData" ... :) ... come lo farai ...? ... perché un semplice ritorno lo restituirà al callback "success" dell'ajax ... e non al di fuori di "handleData" ...
    2016-02-19 16: 02: 18Z
  3. @ Jacques & @pesho hristov Ti sei perso questo punto. Il gestore di invio non è il metodo success, è l'ambito circostante di $.ajax.
    2018-05-28 12: 37: 13Z
  4. @ travnik Non mi sono perso. Se hai preso il contenuto di handleData e lo hai inserito nel metodo di successo, avrebbe funzionato esattamente allo stesso modo ...
    2018-06-13 01: 21: 43Z

La soluzione più semplice è creare una funzione JavaScript e chiamarla per il callback di Ajax success.

 
function callServerAsync(){
    $.ajax({
        url: '...',
        success: function(response) {

            successCallback(response);
        }
    });
}

function successCallback(responseObj){
    // Do something like read the response and show data
    alert(JSON.stringify(responseObj)); // Only applicable to JSON response
}

function foo(callback) {

    $.ajax({
        url: '...',
        success: function(response) {
           return callback(null, response);
        }
    });
}

var result = foo(function(err, result){
          if (!err)
           console.log(result);    
}); 
    
222
2017-01-15 06: 17: 51Z
  1. Non so chi abbia votato come negativo. Ma questo è un lavoro che ha funzionato, infatti ho usato questo approccio per creare un'intera applicazione. Jquery.ajax non restituisce i dati, quindi è meglio utilizzare l'approccio precedente. Se è sbagliato, spiega e suggerisci un modo migliore per farlo.
    2014-03-28 18: 12: 35Z
  2. Scusa, ho dimenticato di lasciare un commento (di solito lo faccio!). L'ho downvoted. I downvotes non indicano la correttezza o la mancanza di fatti, indicano utilità nel contesto o mancanza di. Non trovo la tua risposta utile dato Felix's che già lo spiega solo in modo molto più dettagliato. Nota a margine, perché dovresti stringificare la risposta se si tratta di JSON?
    2014-04-10 09: 18: 07Z
  3. ok .. @Benjamin ho usato stringify, per convertire un oggetto JSON in stringa. E grazie per aver chiarito il tuo punto. Ricorderà di inviare risposte più elaborate.
    2014-04-10 10: 27: 10Z
  4. E se si desidera restituire "responseObj" al di fuori di "successCallback" ... :) ... come lo farai ...? ... perché un semplice ritorno lo restituirà al callback "success" dell'ajax ... e non al di fuori di "successCallback" ...
    2016-02-19 16: 02: 57Z

Risponderò con un fumetto orribile e disegnato a mano. La seconda immagine è la ragione per cui result è undefined nell'esempio di codice.

inserisci la descrizione dell'immagine qui

    
203
2016-08-11 17: 12: 24Z
  1. Un'immagine vale più di mille parole , Persona A - Chiedi alla persona B dettagli per riparare la sua auto, in gira Person B - fa Ajax Call e attende la risposta dal server per i dettagli di riparazione auto, quando la risposta viene ricevuta, la funzione Ajax Success chiama la funzione Person B e passa la risposta come argomento ad essa, la Persona A riceve la risposta.
    2016-10-31 17: 48: 09Z
  2. Sarebbe fantastico se tu aggiungessi delle righe di codice con ogni immagine per illustrare i concetti.
    2018-02-05 00: 32: 58Z

Angular1

Per le persone che utilizzano AngularJS , puoi gestire questa situazione utilizzando Promises.

Qui dice,

  

Le promesse possono essere utilizzate per innervare le funzioni asincrone e consentono di concatenare più funzioni insieme.

Puoi trovare una bella spiegazione qui anche.

Esempio trovato in documenti menzionato sotto.

 
  promiseB = promiseA.then(
    function onSuccess(result) {
      return result + 1;
    }
    ,function onError(err) {
      //Handle error
    }
  );

 // promiseB will be resolved immediately after promiseA is resolved 
 // and its value will be the result of promiseA incremented by 1.

Angolare2 e successive

Nel Angular2, guarda il seguente esempio, ma il suo raccomandato per utilizzare Observables con Angular2.

 
 search(term: string) {
     return this.http
  .get(`https://api.spotify.com/v1/search?q=${term}&type=artist`)
  .map((response) => response.json())
  .toPromise();

}

Puoi consumarlo in questo modo,

 
search() {
    this.searchService.search(this.searchField.value)
      .then((result) => {
    this.result = result.artists.items;
  })
  .catch((error) => console.error(error));
}

Vedi il originale post qui. Ma Typescript non supporta le promesse native di es6 , se vuoi usarle, tu migper questo ho bisogno di un plugin.

Inoltre ecco le promesse specifiche definite qui.

    
147
2017-07-06 04: 45: 28Z
  1. Questo non spiega in che modo le promesse risolveranno comunque questo problema.
    2014-11-04 02: 29: 10Z
  2. Non funziona. promiseB otterrà 'indefinito'
    2014-11-21 15: 35: 47Z
  3. I metodi jQuery e fetch restituiscono entrambe le promesse anche. Suggerirei di rivedere la tua risposta. Anche se jQuery non è esattamente la stessa cosa (allora c'è, ma catch non lo è).
    2015-02-19 19: 24: 49Z

La maggior parte delle risposte qui fornisce suggerimenti utili per quando si ha una singola operazione asincrona, ma a volte, ciò si verifica quando è necessario eseguire un'operazione asincrona per ciascuna voce in un array o altro elenco struttura simile La tentazione è di fare questo:

 
// WRONG
var results = [];
theArray.forEach(function(entry) {
    doSomethingAsync(entry, function(result) {
        results.push(result);
    });
});
console.log(results); // E.g., using them, returning them, etc.

Esempio:

 
// WRONG
var theArray = [1, 2, 3];
var results = [];
theArray.forEach(function(entry) {
    doSomethingAsync(entry, function(result) {
        results.push(result);
    });
});
console.log("Results:", results); // E.g., using them, returning them, etc.

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

Il motivo per cui non funziona è che i callback di

// WRONG
var theArray = [1, 2, 3];
var results = [];
theArray.forEach(function(entry) {
    doSomethingAsync(entry, function(result) {
        results.push(result);
    });
});
console.log("Results:", results); // E.g., using them, returning them, etc.

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
non sono ancora stati eseguiti quando si tenta di utilizzare i risultati.

Quindi, se hai una matrice (o un elenco di qualche tipo) e vuoi eseguire operazioni asincrone per ogni voce, hai due opzioni: Esegui le operazioni in parallelo (sovrapposte) o in serie (una dopo l'altra in sequenza ).

Parallela

Puoi avviarli tutti e tenere traccia del numero di callback che ti aspetti, quindi utilizzare i risultati quando hai ottenuto molti callback:

 
.as-console-wrapper {
  max-height: 100% !important;
}

Esempio:

 doSomethingAsync  
var results = [];
var expecting = theArray.length;
theArray.forEach(function(entry, index) {
    doSomethingAsync(entry, function(result) {
        results[index] = result;
        if (--expecting === 0) {
            // Done!
            console.log("Results:", results); // E.g., using the results
        }
    });
});

(Potremmo eliminare

var theArray = [1, 2, 3];
var results = [];
var expecting = theArray.length;
theArray.forEach(function(entry, index) {
    doSomethingAsync(entry, function(result) {
        results[index] = result;
        if (--expecting === 0) {
            // Done!
            console.log("Results:", results); // E.g., using the results
        }
    });
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}
e usare solo il
var theArray = [1, 2, 3];
var results = [];
var expecting = theArray.length;
theArray.forEach(function(entry, index) {
    doSomethingAsync(entry, function(result) {
        results[index] = result;
        if (--expecting === 0) {
            // Done!
            console.log("Results:", results); // E.g., using the results
        }
    });
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
, ma questo ci lascia aperti alla possibilità che
.as-console-wrapper {
  max-height: 100% !important;
}
venga modificato mentre le chiamate sono in sospeso ...)

Notate come usiamo il expecting del results.length === theArray.length per salvare il risultato in theArray nella stessa posizione della voce a cui si riferisce, anche se i risultati arrivano fuori ordine (poiché le chiamate asincrone non necessariamente si completano nell'ordine in cui sono stati avviati).

E se fosse necessario restituire quei risultati di una funzione? Come hanno sottolineato le altre risposte, non puoi; devi accettare la funzione e chiamare un callback (o restituire un Promessa). Ecco una versione di callback:

 index

Esempio:

 forEach  results

Oppure ecco una versione che restituisce un

function doSomethingWith(theArray, callback) {
    var results = [];
    var expecting = theArray.length;
    theArray.forEach(function(entry, index) {
        doSomethingAsync(entry, function(result) {
            results[index] = result;
            if (--expecting === 0) {
                // Done!
                callback(results);
            }
        });
    });
}
doSomethingWith(theArray, function(results) {
    console.log("Results:", results);
});
:  
function doSomethingWith(theArray, callback) {
    var results = [];
    var expecting = theArray.length;
    theArray.forEach(function(entry, index) {
        doSomethingAsync(entry, function(result) {
            results[index] = result;
            if (--expecting === 0) {
                // Done!
                callback(results);
            }
        });
    });
}
doSomethingWith([1, 2, 3], function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

Naturalmente, se

function doSomethingWith(theArray, callback) {
    var results = [];
    var expecting = theArray.length;
    theArray.forEach(function(entry, index) {
        doSomethingAsync(entry, function(result) {
            results[index] = result;
            if (--expecting === 0) {
                // Done!
                callback(results);
            }
        });
    });
}
doSomethingWith([1, 2, 3], function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
ci ha passato degli errori, dovremmo usare
.as-console-wrapper {
  max-height: 100% !important;
}
per rifiutare la promessa quando abbiamo ricevuto un errore.)

Esempio:

 Promise  
function doSomethingWith(theArray) {
    return new Promise(function(resolve) {
        var results = [];
        var expecting = theArray.length;
        theArray.forEach(function(entry, index) {
            doSomethingAsync(entry, function(result) {
                results[index] = result;
                if (--expecting === 0) {
                    // Done!
                    resolve(results);
                }
            });
        });
    });
}
doSomethingWith(theArray).then(function(results) {
    console.log("Results:", results);
});

(O in alternativa, potresti creare un wrapper per doSomethingAsync che restituisce una promessa, e poi fai il seguente ...)

Se il reject ti offre una Promessa , puoi utilizzare

:  

function doSomethingWith(theArray) {
    return new Promise(function(resolve) {
        var results = [];
        var expecting = theArray.length;
        theArray.forEach(function(entry, index) {
            doSomethingAsync(entry, function(result) {
                results[index] = result;
                if (--expecting === 0) {
                    // Done!
                    resolve(results);
                }
            });
        });
    });
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}

Se sai che

.as-console-wrapper {
  max-height: 100% !important;
}
ignorerà un secondo e un terzo argomento, puoi passarlo direttamente a doSomethingAsync (doSomethingAsync chiama la sua callback con tre argomenti, ma la maggior parte delle persone usa solo il primo il più delle volte):  Promise.all

Esempio:

 
function doSomethingWith(theArray) {
    return Promise.all(theArray.map(function(entry) {
        return doSomethingAsync(entry);
    }));
}
doSomethingWith(theArray).then(function(results) {
    console.log("Results:", results);
});
doSomethingAsync

Nota che map risolve la sua promessa con una serie di risultati di tutte le promesse che gli hai dato quando sono tutte risolte, o rifiuta la sua promessa quando prima delle promesse che gli dai rifiuti .

Serie

Supponiamo che tu non voglia che le operazioni siano in parallelo? Se si desidera eseguirli uno dopo l'altro, è necessario attendere il completamento di ciascuna operazione prima di avviare il successivo. Ecco un esempio di una funzione che lo fa e chiama un callback con il risultato:

 map

(Dato che stiamo lavorando in serie, possiamo usare

function doSomethingWith(theArray) {
    return Promise.all(theArray.map(doSomethingAsync));
}
doSomethingWith(theArray).then(function(results) {
    console.log("Results:", results);
});
solo perché sappiamo che non otterremo risultati in modo errato. In precedenza avremmo potuto usare
function doSomethingWith(theArray) {
    return Promise.all(theArray.map(doSomethingAsync));
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
.as-console-wrapper {
  max-height: 100% !important;
}
, ma in alcuni dei seguenti esempi non abbiamo un indice da usare.)

Esempio:

 
function doSomethingWith(theArray) {
    return Promise.all(theArray.map(doSomethingAsync));
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
 
.as-console-wrapper {
  max-height: 100% !important;
}

(Oppure, ancora una volta, crea un wrapper per Promise.all che ti dia una promessa e faccia il seguente ...)

Se il

function doSomethingWith(theArray, callback) {
    var results = [];
    doOne(0);
    function doOne(index) {
        if (index < theArray.length) {
            doSomethingAsync(theArray[index], function(result) {
                results.push(result);
                doOne(index + 1);
            });
        } else {
            // Done!
            callback(results);
        }
    }
}
doSomethingWith(theArray, function(results) {
    console.log("Results:", results);
});
ti offre una Promessa, se puoi usare la sintassi ES2017 + (magari con un transpiler come Babel ), puoi utilizza una funzione results.push(result) con results[index] = result; e
function doSomethingWith(theArray, callback) {
    var results = [];
    doOne(0);
    function doOne(index) {
        if (index < theArray.length) {
            doSomethingAsync(theArray[index], function(result) {
                results.push(result);
                doOne(index + 1);
            });
        } else {
            // Done!
            callback(results);
        }
    }
}
doSomethingWith([1, 2, 3], function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}
:  
function doSomethingWith(theArray, callback) {
    var results = [];
    doOne(0);
    function doOne(index) {
        if (index < theArray.length) {
            doSomethingAsync(theArray[index], function(result) {
                results.push(result);
                doOne(index + 1);
            });
        } else {
            // Done!
            callback(results);
        }
    }
}
doSomethingWith([1, 2, 3], function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}

Esempio:

 
.as-console-wrapper {
  max-height: 100% !important;
}
 doSomethingAsync

Se non è possibile utilizzare la sintassi ES2017 + (ancora), è possibile utilizzare una variante su " Promise reduce pattern " (questo è più complesso del solito Promise riduci perché non passiamo il risultato da uno a quello successivo, ma invece raccogliamo i risultati in un array):

 doSomethingAsync

Esempio:

 async  for-of

... che è meno ingombrante con ES2015 + freccia funzioni :

 await

Esempio:

 
async function doSomethingWith(theArray) {
    const results = [];
    for (const entry of theArray) {
        results.push(await doSomethingAsync(entry));
    }
    return results;
}
doSomethingWith(theArray).then(results => {
    console.log("Results:", results);
});
 
async function doSomethingWith(theArray) {
    const results = [];
    for (const entry of theArray) {
        results.push(await doSomethingAsync(entry));
    }
    return results;
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
.as-console-wrapper {
  max-height: 100% !important;
}
    
131
2019-04-26 07: 50: 55Z
  1. Potresti spiegare come funziona la parte
    async function doSomethingWith(theArray) {
        const results = [];
        for (const entry of theArray) {
            results.push(await doSomethingAsync(entry));
        }
        return results;
    }
    doSomethingWith([1, 2, 3]).then(function(results) {
        console.log("Results:", results);
    });
    
    function doSomethingAsync(value) {
        console.log("Starting async operation for " + value);
        return new Promise(function(resolve) {
            setTimeout(function() {
                console.log("Completing async operation for " + value);
                resolve(value * 2);
            }, Math.floor(Math.random() * 200));
        });
    }
    del codice? La versione di callback della tua soluzione funziona perfettamente per me, ma non capisco come, con questa affermazione, stai controllando il numero di risposte completate. Apprezzo che sia solo la mancanza di conoscenza da parte mia. Esiste un modo alternativo di controllo?
    2017-05-28 10: 21: 35Z
  2. @ Sarah:
    .as-console-wrapper {
      max-height: 100% !important;
    }
    inizia con il valore di
    function doSomethingWith(theArray) {
        return theArray.reduce(function(p, entry) {
            return p.then(function(results) {
                return doSomethingAsync(entry).then(function(result) {
                    results.push(result);
                    return results;
                });
            });
        }, Promise.resolve([]));
    }
    doSomethingWith(theArray).then(function(results) {
        console.log("Results:", results);
    });
    
    , ovvero quante richieste faremo. Sappiamo che il callback non verrà chiamato fino a quando tutte queste richieste non verranno avviate. Nel callback,
    function doSomethingWith(theArray) {
        return theArray.reduce(function(p, entry) {
            return p.then(function(results) {
                return doSomethingAsync(entry).then(function(result) {
                    results.push(result);
                    return results;
                });
            });
        }, Promise.resolve([]));
    }
    doSomethingWith([1, 2, 3]).then(function(results) {
        console.log("Results:", results);
    });
    
    function doSomethingAsync(value) {
        console.log("Starting async operation for " + value);
        return new Promise(function(resolve) {
            setTimeout(function() {
                console.log("Completing async operation for " + value);
                resolve(value * 2);
            }, Math.floor(Math.random() * 200));
        });
    }
    .as-console-wrapper {
      max-height: 100% !important;
    }
    fa questo: 1. Decrementi
    function doSomethingWith(theArray) {
        return theArray.reduce(function(p, entry) {
            return p.then(function(results) {
                return doSomethingAsync(entry).then(function(result) {
                    results.push(result);
                    return results;
                });
            });
        }, Promise.resolve([]));
    }
    doSomethingWith([1, 2, 3]).then(function(results) {
        console.log("Results:", results);
    });
    
    function doSomethingAsync(value) {
        console.log("Starting async operation for " + value);
        return new Promise(function(resolve) {
            setTimeout(function() {
                console.log("Completing async operation for " + value);
                resolve(value * 2);
            }, Math.floor(Math.random() * 200));
        });
    }
    (abbiamo ricevuto una risposta, quindi ci aspettiamo una risposta in meno) e se il valore dopo il decremento è 0 (non lo siamo aspettandoci ulteriori risposte), abbiamo finito!
    2017-05-28 18: 31: 11Z
  3. @ PatrickRoberts - Grazie !! Sì, errore di copia e incolla, quel secondo argomento è stato completamente ignorato in quell'esempio (che è l'unica ragione per cui non ha fallito, poiché come hai sottolineato,
    .as-console-wrapper {
      max-height: 100% !important;
    }
    non esisteva). :-) Risolto il problema.
    2019-04-26 07: 51: 43Z

Dai un'occhiata a questo esempio:

 
function doSomethingWith(theArray) {
    return theArray.reduce((p, entry) => p.then(results => doSomethingAsync(entry).then(result => {
        results.push(result);
        return results;
    })), Promise.resolve([]));
}
doSomethingWith(theArray).then(results => {
    console.log("Results:", results);
});

Come puoi vederee

function doSomethingWith(theArray) {
    return theArray.reduce((p, entry) => p.then(results => doSomethingAsync(entry).then(result => {
        results.push(result);
        return results;
    })), Promise.resolve([]));
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
.as-console-wrapper {
  max-height: 100% !important;
}
è che restituisce risolto promessa (è risolto quando restituisce
function doSomethingWith(theArray) {
    return theArray.reduce((p, entry) => p.then(results => doSomethingAsync(entry).then(result => {
        results.push(result);
        return results;
    })), Promise.resolve([]));
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
). Quindi attendi che la richiesta $http.get sia completata e quindi console.log (res.joke) viene eseguito (come un normale flusso asincrono).

Questo è il plnkr:

http://embed.plnkr.co/XlNR7HpCaIhJxskMJfSg/

ES6 way (async - await)

 
.as-console-wrapper {
  max-height: 100% !important;
}
    
102
2018-11-23 12: 19: 11Z

Un altro approccio per restituire un valore da una funzione asincrona consiste nel passare un oggetto che archivierà il risultato dalla funzione asincrona.

Ecco un esempio dello stesso:

 if (--expecting === 0)

Sto usando l'oggetto expecting per memorizzare il valore durante l'operazione asincrona. Ciò consente al risultato di essere disponibile anche dopo il lavoro asincrono.

Uso molto questo approccio. Sarei interessato a sapere quanto bene questo approccio funziona dove è coinvolto il cablaggio del risultato attraverso moduli consecutivi.

    
88
2016-12-17 12: 55: 25Z
  1. Non c'è niente di speciale nell'usare un oggetto qui. Funzionerebbe anche se assegnassi la risposta direttamente a array.length. Funziona perché stai leggendo la variabile dopo la funzione asincrona è completata.
    2015-09-02 13: 18: 11Z

Questo è uno dei luoghi in cui associazione dei dati in due modi utilizzato in molti nuovi framework JavaScript funzionerà perfettamente per te ...

Quindi, se utilizzi Angolare, Reagisci o qualsiasi altro framework che associazione di dati in due modi o concetto di negozio questo problema viene semplicemente risolto per te, quindi in parole semplici, il tuo risultato è if (--expecting === 0) nella prima fase, quindi hai expecting prima di ricevere i dati, quindi non appena ottieni il risultato, sarà aggiornato e verrà assegnato al nuovo valore che risposta della tua chiamata Ajax ...

Ma come puoi farlo in puro javascript o jQuery , ad esempio, come hai chiesto in questa domanda?

Puoi utilizzare una richiamata ​​strong>, promessa e recentemente osservabile per gestirla per te, ad esempio nelle promesse abbiamo alcune funzioni come il successo () o poi () che verrà eseguito quando i tuoi dati sono pronti per te, lo stesso con la funzione callback o subscribe su osservabile .

Ad esempio nel tuo caso che stai utilizzando jQuery , puoi fare qualcosa di simile a questo:

 results

Per ulteriori informazioni, studia le promises e observables che sono i nuovi modi per fare questo async stuff.

    
88
2019-05-21 14: 22: 58Z
  1. Questo va bene per l'ambito globale, ma in alcuni contesto del modulo probabilmente si vuole garantire il giusto contesto per il callback, ad es.
    var app = angular.module('plunker', []);
    
    app.controller('MainCtrl', function($scope,$http) {
    
        var getJoke = function(){
            return $http.get('http://api.icndb.com/jokes/random').then(function(res){
                return res.data.value;  
            });
        }
    
        getJoke().then(function(res) {
            console.log(res.joke);
        });
    });
    
    2017-07-24 06: 14: 12Z
  2. Questo in realtà non è corretto in quanto React è un collegamento dati unidirezionale
    2018-05-04 15: 57: 05Z
  3. @ MatthewBrent non sei sbagliato, ma non è corretto, i puntelli di React sono oggetto e se modificati cambiano nell'intera applicazione, ma non è un modo che lo sviluppatore di React consiglia per usarlo ...
    2018-05-14 07: 34: 28Z

Mentre promesse e callback funzionano bene in mqualsiasi situazione, è un dolore nella parte posteriore esprimere qualcosa come:

 getJoke

Finiresti per passare attraverso res.data.value; controlla se

(function(){
  async function getJoke(){
    let response = await fetch('http://api.icndb.com/jokes/random');
    let data = await response.json();
    return data.value;
  }

  getJoke().then((joke) => {
    console.log(joke);
  });
})();
non è definito o no e chiama di conseguenza la richiamata.  
var async = require("async");

// This wires up result back to the caller
var result = {};
var asyncTasks = [];
asyncTasks.push(function(_callback){
    // some asynchronous operation
    $.ajax({
        url: '...',
        success: function(response) {
            result.response = response;
            _callback();
        }
    });
});

async.parallel(asyncTasks, function(){
    // result is available after performing asynchronous operation
    console.log(result)
    console.log('Done');
});

Anche se va bene in piccoli esempi diventa fastidioso quando si hanno molti casi simili e la gestione degli errori è coinvolta.

result aiuta a risolvere il problema.

 result

Puoi controllare il progetto qui .

    
80
2016-05-09 13: 02: 40Z
  1. @ recurf - Non è il mio progetto. Potresti provare a utilizzare il loro tracker dei problemi.
    2017-01-20 15: 22: 44Z
  2. 2017-06-07 03: 19: 47Z
  3. È ancora pertinente?
    2018-03-18 19: 43: 54Z
  4. Puoi usare undefined se stai usando alcune delle versioni più recenti del nodo. Se qualcuno è bloccato con versioni precedenti, può utilizzare questo metodo.
    2018-03-20 08: 18: 27Z

Risposta breve è, devi implementare un callback come questo:

 result = undefined     
76
2016-08-09 18: 32: 41Z

Il seguente esempio che ho scritto mostra come

  • Gestisce le chiamate HTTP asincrone;
  • Attendi la risposta da ciascuna chiamata API;
  • Utilizza Promise modello;
  • Utilizza Promise.all pattern per unire più chiamate HTTP;

Questo esempio di lavoro è autonomo. Definirà un oggetto di richiesta semplice che utilizza l'oggetto

$(document).ready(function(){
    function foo() {
        $.ajax({url: "api/data", success: function(data){
            fooDone(data); //after we have data, we pass it to fooDone
        }});
    };

    function fooDone(data) {
        console.log(data); //fooDone has the data and console.log it
    };

    foo(); //call happens here
});
della finestra per effettuare chiamate. Definirà una semplice funzione per attendere il completamento di un gruppo di promesse.

Contesto. L'esempio sta interrogando l'endpoint Spotify Web API per cercare oggetti $.ajax({url: "api/data", success: fooDone.bind(this)}); per un determinato insieme di stringhe di query:

 
if (!name) {
  name = async1();
}
async2(name);

Per ogni articolo, una nuova Promessa genera un blocco - async1, analizza il risultato, pianifica una nuova serie di promesse basate sull'array dei risultati, ovvero un elenco di oggetti Spotify name ed esegue la nuova chiamata HTTP all'interno del

async1(name, callback) {
  if (name)
    callback(name)
  else {
    doSomething(callback)
  }
}

async1(name, async2)
in modo asincrono.

Puoi quindi vedere una struttura Promessa nidificata, che ti consente di generare più e nettamente chiamate HTTP annidate asincrone e unire i risultati di ogni sottoinsieme di chiamate attraverso Fibers.

Nota ​​strong> Le recenti API Spotify

var Fiber = require('fibers')

function async1(container) {
  var current = Fiber.current
  var result
  doSomething(function(name) {
    result = name
    fiber.run()
  })
  Fiber.yield()
  return result
}

Fiber(function() {
  var name
  if (!name) {
    name = async1()
  }
  async2(name)
  // Make any number of async calls from here
}
richiederanno un token di accesso da specificare nelle intestazioni delle richieste:

 async-await

Quindi, per eseguire l'esempio seguente devi inserire il tuo token di accesso nelle intestazioni della richiesta:

 
function callback(response) {
    // Here you can do what ever you want with the response object.
    console.log(response);
}

$.ajax({
    url: "...",
    success: callback
});

Ho discusso ampiamente di questa soluzione qui .

    
74
2018-11-28 16: 42: 31Z

2017 answer: ora puoi fare esattamente ciò che desideri in ogni browser e nodo corrente

Questo è abbastanza semplice:

  • Restituire una promessa
  • Utilizza "attendere" , che dirà a JavaScript di attendere la promessa di essere risolto in un valore (come la risposta HTTP)
  • Aggiungi la parola chiave "async" alla funzione genitore

Ecco una versione funzionante del tuo codice:

 XMLHttpRequest

attendere è supportato in tutti i browser correnti e nel nodo 8

    
71
2018-10-01 10: 06: 17Z
  1. Sfortunatamente, funziona solo con funzioni che restituiscono promesse, ad esempio non funziona con l'API Node.js, che utilizza le callback. E non lo consiglierei di usarlo senza Babel, perché non tutti usano "browser correnti".
    2017-06-08 06: 47: 10Z
  2. @ MichałPerłakowski node 8 include nodejs. org /api /util.html # util_util_promisify_original che può essere utilizzato per rendere promesse le API node.js. Se hai tempo e denaro per supportare i browser non correnti, ovviamente dipende dalla tua situazione.
    2017-06-09 18: 28: 16Z
  3. IE 11 è ancora un browser corrente nel 2018, purtroppo e non supporta playlist
    2018-10-04 14: 51: 34Z
  4. IE11 non è un browser corrente. E 'stato rilasciato 5 anni fa, ha una quota di mercato mondiale del 2,5% secondo caniuse, e a meno che qualcuno stia raddoppiando il tuo budget per ignorare tutta la tecnologia corrente, allora non vale la pena di molte persone.
    2018-10-04 14: 57: 29Z

Puoi usare questa libreria personalizzata (scritta usando Promise) per effettuare una chiamata remota.

 
[
 "search?type=playlist&q=%22doom%20metal%22",
 "search?type=playlist&q=Adele"
]

Esempio di utilizzo semplice:

 ExecutionBlock     
63
2016-12-17 10: 59: 13Z
  

Js è un singolo thread.

Il browser può essere diviso in tre parti:

1) Event Loop

2) API Web

3) Coda eventi

Event Loop viene eseguito per sempre come tipo di loop infinito.Event Queue è dove vengono spinte tutte le tue funzioni su alcuni eventi (esempio: click) questo è uno ad uno eseguito dalla coda e inserito nel loop Event che esegue questa funzione e prepara se stesso per quello successivo dopo che viene eseguito il primo. Ciò significa che l'esecuzione di una funzione non inizia finché la funzione prima di essa non viene eseguita nel ciclo di eventi.

Ora pensiamo di aver spinto due funzioni in una coda per ottenere un dato dal server e un altro utilizza quei dati. Abbiamo prima inserito la funzione serverRequest () in coda poi la funzione utiliseData (). La funzione serverRequest entra nel ciclo degli eventi e fa una chiamata al server poiché non sappiamo mai quanto tempo ci vorrà per ottenere i dati dal server ci si aspetta che questo processo richieda tempo e così abbiamo occupato il nostro ciclo di eventi appeso così alla nostra pagina, ecco dove le API Web entrano in ruolo prendono questa funzione dal ciclo degli eventi e si occupa del server rendendo libero il ciclo degli eventi in modo che possiamo eseguire la prossima funzione da coda. La funzione successiva in coda è utiliseData () che va in loop ma a causa della mancanza di dati disponibili va sprecata e l'esecuzione della funzione successiva continua fino alla fine della coda. (Si chiama chiamata asincrona cioè possiamo fare qualcos'altro fino a quando ottieni dati)

Supponiamo che la nostra funzione serverRequest () abbia un'istruzione return in un codice, quando recuperiamo i dati dall'API Web del server la inseriremo in coda alla fine della coda. Dato che alla fine vengono sospesi in coda, non possiamo utilizzare i suoi dati poiché non è rimasta alcuna funzione nella nostra coda per utilizzare questi dati. Quindi non è possibile restituire qualcosa dalla chiamata asincrona.

Quindi la soluzione a questo è callback o promessa .

Un'immagine di una delle risposte qui, spiega correttamente l'uso della richiamata ... Diamo la nostra funzione (funzione che utilizza i dati restituiti dal server) per funzionare sul server di chiamata.

 CallBack

 user

Nel mio codice è chiamato come

 ExecutionProfileBlock

Leggi qui per i nuovi metodi in ECMA (2016/17) per effettuare una chiamata asincrona (@Felix Kling Answer on Top) https://stackoverflow.com/a/14220323/7579856

    
58
2018-03-16 16: 48: 55Z

Un'altra soluzione è quella di eseguire il codice tramite l'esecutore sequenziale nsynjs .

Se la funzione sottostante è promessa

nsynjs valuterà tutte le promesse in sequenza e metterà i risultati della promessa nella proprietà Promise.all:

 search

Se la funzione sottostante non è promessa

Passaggio 1. Funzione di avvolgimento con richiamata nel wrapper nsynjs-aware (se ha una versione promessa, è possibile saltare questo passaggio):

 
-H "Authorization: Bearer {your access token}" 

Passaggio 2. Metti la logica sincrona in funzione:

 
var spotifyAccessToken = "YourSpotifyAccessToken";
var console = {
    log: function(s) {
        document.getElementById("console").innerHTML += s + "<br/>"
    }
}

// Simple XMLHttpRequest
// based on https://davidwalsh.name/xmlhttprequest
SimpleRequest = {
    call: function(what, response) {
        var request;
        if (window.XMLHttpRequest) { // Mozilla, Safari, ...
            request = new XMLHttpRequest();
        } else if (window.ActiveXObject) { // Internet Explorer
            try {
                request = new ActiveXObject('Msxml2.XMLHTTP');
            }
            catch (e) {
                try {
                  request = new ActiveXObject('Microsoft.XMLHTTP');
                } catch (e) {}
            }
        }

        // State changes
        request.onreadystatechange = function() {
            if (request.readyState === 4) { // Done
                if (request.status === 200) { // Complete
                    response(request.responseText)
                }
                else
                    response();
            }
        }
        request.open('GET', what, true);
        request.setRequestHeader("Authorization", "Bearer " + spotifyAccessToken);
        request.send(null);
    }
}

//PromiseAll
var promiseAll = function(items, block, done, fail) {
    var self = this;
    var promises = [],
                   index = 0;
    items.forEach(function(item) {
        promises.push(function(item, i) {
            return new Promise(function(resolve, reject) {
                if (block) {
                    block.apply(this, [item, index, resolve, reject]);
                }
            });
        }(item, ++index))
    });
    Promise.all(promises).then(function AcceptHandler(results) {
        if (done) done(results);
    }, function ErrorHandler(error) {
        if (fail) fail(error);
    });
}; //promiseAll

// LP: deferred execution block
var ExecutionBlock = function(item, index, resolve, reject) {
    var url = "https://api.spotify.com/v1/"
    url += item;
    console.log( url )
    SimpleRequest.call(url, function(result) {
        if (result) {

            var profileUrls = JSON.parse(result).playlists.items.map(function(item, index) {
                return item.owner.href;
            })
            resolve(profileUrls);
        }
        else {
            reject(new Error("call error"));
        }
    })
}

arr = [
    "search?type=playlist&q=%22doom%20metal%22",
    "search?type=playlist&q=Adele"
]

promiseAll(arr, function(item, index, resolve, reject) {
    console.log("Making request [" + index + "]")
    ExecutionBlock(item, index, resolve, reject);
}, function(results) { // Aggregated results

    console.log("All profiles received " + results.length);
    //console.log(JSON.stringify(results[0], null, 2));

    ///// promiseall again

    var ExecutionProfileBlock = function(item, index, resolve, reject) {
        SimpleRequest.call(item, function(result) {
            if (result) {
                var obj = JSON.parse(result);
                resolve({
                    name: obj.display_name,
                    followers: obj.followers.total,
                    url: obj.href
                });
            } //result
        })
    } //ExecutionProfileBlock

    promiseAll(results[0], function(item, index, resolve, reject) {
        //console.log("Making request [" + index + "] " + item)
        ExecutionProfileBlock(item, index, resolve, reject);
    }, function(results) { // aggregated results
        console.log("All response received " + results.length);
        console.log(JSON.stringify(results, null, 2));
    }

    , function(error) { // Error
        console.log(error);
    })

    /////

  },
  function(error) { // Error
      console.log(error);
  });
<div id="console" />

Passaggio 3. Eseguire la funzione in modo sincrono tramite nsynjs:

 
(async function(){

var response = await superagent.get('...')
console.log(response)

})()

Nsynjs valuterà tutti gli operatori e le espressioni passo dopo passo, sospendendo l'esecuzione nel caso in cui il risultato di qualche funzione lenta non sia pronto.

Altri esempi qui: https://github.com/amaksr/nsynjs/tree/master /esempi

    
57
2019-04-03 16: 00: 18Z
  1. Questo è interessante. Mi piace come permette di codificare le chiamate asincrone come faresti in altre lingue. Ma tecnicamente non è vero JavaScript?
    2017-06-16 23: 55: 06Z

È un problema molto comune che affrontiamo mentre lottiamo con i "misteri" di JavaScript. Lasciatemi provare a demistificare questo mistero oggi.

Iniziamo con una semplice funzione JavaScript:

 await/async

Questa è una semplice chiamata di funzione sincrona (in cui ogni riga di codice è "completata con il suo lavoro" prima della successiva in sequenza) e il risultato è lo stesso di quanto previsto.

Ora aggiungiamo un po 'di twist, introducendo un piccolo ritardo nella nostra funzione, in modo che tutte le linee di codice non siano "finite" in sequenza. Pertanto, emulerà il comportamento asincrono della funzione:

 
function $http(apiConfig) {
    return new Promise(function (resolve, reject) {
        var client = new XMLHttpRequest();
        client.open(apiConfig.method, apiConfig.url);
        client.send();
        client.onload = function () {
            if (this.status >= 200 && this.status < 300) {
                // Performs the function "resolve" when this.status is equal to 2xx.
                // Your logic here.
                resolve(this.response);
            }
            else {
                // Performs the function "reject" when this.status is different than 2xx.
                reject(this.statusText);
            }
        };
        client.onerror = function () {
            reject(this.statusText);
        };
    });
}

Quindi ci sei andato, quel ritardo ha solo rotto la funzionalità che ci aspettavamo! Ma cosa è successo esattamente? Beh, in realtà è piuttosto logico se si guarda il codice. la funzione

$http({
    method: 'get',
    url: 'google.com'
}).then(function(response) {
    console.log(response);
}, function(error) {
    console.log(error)
});
, al momento dell'esecuzione, non restituisce nulla (il valore restituito è quindi
 function doAjax(callbackFunc, method, url) {
  var xmlHttpReq = new XMLHttpRequest();
  xmlHttpReq.open(method, url);
  xmlHttpReq.onreadystatechange = function() {

      if (xmlHttpReq.readyState == 4 && xmlHttpReq.status == 200) {
        callbackFunc(xmlHttpReq.responseText);
      }


  }
  xmlHttpReq.send(null);

}
), ma avvia un timer, che esegue una funzione dopo 1s per restituire 'wohoo'. Ma come puoi vedere, il valore che viene assegnato alla barra è la roba immediatamente restituita da foo (), non qualsiasi altra cosa che viene dopo.

Quindi, come affrontiamo questo problema?

Chiediamo alla nostra funzione una PROMESSA . La promessa riguarda davvero cosa significa: significa che la funzione ti garantisce di fornire qualsiasi risultato che otterrà in futuro. quindi vediamo in azione per il nostro piccolo problema sopra:

 
function loadMyJson(categoryValue){
  if(categoryValue==="veg")
  doAjax(print,"GET","http://localhost:3004/vegetables");
  else if(categoryValue==="fruits")
  doAjax(print,"GET","http://localhost:3004/fruits");
  else 
  console.log("Data not found");
}

Quindi, il sommario è - per affrontare le funzioni asincrone come le chiamate basate su un jax, ecc., puoi usare una promessa al valore data (che intendi restituire). Quindi, in breve, risolvi valore invece di ritorno , in funzioni asincrone.

UPDATE (promette con async /await)

Oltre all'utilizzo del

function synchronousCode() {

    var getURL = function(url) {
        return window.fetch(url).data.text().data;
    };
    
    var url = 'https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js';
    console.log('received bytes:',getURL(url).length);
    
};

nsynjs.run(synchronousCode,{},function(){
    console.log('synchronousCode done');
});
<script src="https://rawgit.com/amaksr/nsynjs/master/nsynjs.js"></script>
per lavorare con le promesse, esiste un ulteriore approccio. L'idea è di riconoscere una funzione asincrona e quindi attendere le promesse da risolvere, prima di passare alla riga successiva del codice. È ancora solo il
var ajaxGet = function (ctx,url) {
    var res = {};
    var ex;
    $.ajax(url)
    .done(function (data) {
        res.data = data;
    })
    .fail(function(e) {
        ex = e;
    })
    .always(function() {
        ctx.resume(ex);
    });
    return res;
};
ajaxGet.nsynjsHasCallback = true;
sotto il cofano, ma con un diverso approccio sintattico. Per rendere le cose più chiare, puoi trovare un confronton di seguito:

quindi /catch versione:

 
function process() {
    console.log('got data:', ajaxGet(nsynjsCtx, "data/file1.json").data);
}

async /attende la versione:

 
nsynjs.run(process,this,function () {
    console.log("synchronous function finished");
});
    
43
2018-10-09 17: 33: 32Z
  1. è ancora considerato il modo migliore per restituire un valore da una promessa o asincrona /attendi?
    2018-09-26 20: 58: 21Z
  2. @ edwardsmarkf Personalmente non penso che ci sia un modo migliore in quanto tale. Io uso promesse con then /catch, async /await e generatori per porzioni asincrone del mio codice. Dipende in gran parte dal contesto di utilizzo.
    2018-10-03 16: 12: 28Z

ECMAScript 6 ha 'generatori' che ti permettono di programmare facilmente in uno stile asincrono.

 
function foo(){
// do something 
 return 'wohoo';
}

let bar = foo(); // bar is 'wohoo' here

Per eseguire il codice sopra, fai questo:

 
function foo(){
 setTimeout( ()=>{
   return 'wohoo';
  }, 1000 )
}

let bar = foo() // bar is undefined here

Se devi scegliere come target i browser che non supportano ES6, puoi eseguire il codice tramite Babel o closure-compiler per generare ECMAScript 5.

Il callback foo() viene avvolto in una matrice e destrutturato quando vengono letti in modo che il pattern possa far fronte a callback con più argomenti. Ad esempio con nodo fs :

 undefined     
37
2018-07-31 10: 35: 51Z
  1. Sarei interessato a sapere perché questo è down-votato. Sembra una buona risposta semplice che non è stata precedentemente fornita.
    2018-02-17 17: 49: 50Z

Ecco alcuni approcci per lavorare con le richieste asincrone:

  1. Oggetto Promessa browser
  2. Q - Una libreria promessa per JavaScript
  3. A + Promises.js
  4. jQuery differito
  5. API XMLHttpRequest
  6. Uso del concetto di callback - Come implementazione in prima risposta ​​li>

Esempio: implementazione differita di jQuery per lavorare con più richieste

 
function foo(){
   return new Promise( (resolve, reject) => { // I want foo() to PROMISE me something
    setTimeout ( function(){ 
      // promise is RESOLVED , when execution reaches this line of code
       resolve('wohoo')// After 1 second, RESOLVE the promise with value 'wohoo'
    }, 1000 )
  })
}

let bar ; 
foo().then( res => {
 bar = res;
 console.log(bar) // will print 'wohoo'
});
    
36
2017-06-22 09: 31: 40Z

Risposta breve : il metodo resolve restituisce immediatamente, mentre la chiamata then/catch viene eseguita in modo asincrono dopo che la funzione restituisce . Il problema è quindi come o dove memorizzare i risultati recuperati dalla chiamata asincrona una volta che ritorna.

Diverse soluzioni sono state fornite in questo thread. Forse il modo più semplice è passare un oggetto al metodo promises e archiviare i risultati in un membro di quell'oggetto al termine della chiamata asincrona.

 
function fetchUsers(){
   let users = [];
   getUsers()
   .then(_users => users = _users)
   .catch(err =>{
      throw err
   })
   return users;
 }

Si noti che la chiamata a

  async function fetchUsers(){
     try{
        let users = await getUsers()
        return users;
     }
     catch(err){
        throw err;
     }
  }
non restituirà ancora nulla di utile. Tuttavia, il risultato della chiamata asincrona verrà ora archiviato in
function* myGenerator() {
    const callback = yield;
    let [response] = yield $.ajax("https://stackoverflow.com", {complete: callback});
    console.log("response is:", response);

    // examples of other things you can do
    yield setTimeout(callback, 1000);
    console.log("it delayed for 1000ms");
    while (response.statusText === "error") {
        [response] = yield* anotherGenerator();
    }
}
.     
33
2015-09-23 22: 52: 03Z
  1. Mentre funziona, non è molto meglio dell'assegnazione a una variabile globale.
    2015-09-23 22: 53: 07Z

Utilizzare una funzione

const gen = myGenerator(); // Create generator
gen.next(); // Start it
gen.next((...args) => gen.next([...args])); // Set its callback function
all'interno del ...args successo. Prova in questo modo. È semplice e facile da capire.  
const [err, data] = yield fs.readFile(filePath, "utf-8", callback);
    
33
2017-11-17 09: 15: 45Z

Ci troviamo in un universo che sembra progredire lungo una dimensione che chiamiamo "tempo". Non capiamo veramente che tempo sia, ma abbiamo sviluppato astrazioni e vocabolario che ci permettono di ragionare e parlarne: "passato", "presente", "futuro", "prima", "dopo".

I sistemi informatici che costruiamo - sempre di più - hanno il tempo come una dimensione importante. Certe cose sono programmate per accadere in futuro. Poi altre cose devono accadere dopo che queste prime cose si sono verificate. Questa è la nozione base chiamata "asincronicità". Nel nostro mondo sempre più collegato in rete, il caso più comune di asincronismo è che alcuni sistemi remoti rispondano ad alcune richieste.

Considera un esempio. Chiami il lattaio e ordina del latte. Quando arriva, vuoi metterlo nel tuo caffè. Non puoi mettere il latte nel tuo caffè adesso, perché non è ancora qui. Devi aspettare che arrivi prima di metterlo nel tuo caffè. In altre parole, il seguente non funzionerà:

 
var App = App || {};

App = {
    getDataFromServer: function(){

      var self = this,
                 deferred = $.Deferred(),
                 requests = [];

      requests.push($.getJSON('request/ajax/url/1'));
      requests.push($.getJSON('request/ajax/url/2'));

      $.when.apply(jQuery, requests).done(function(xhrResponse) {
        return deferred.resolve(xhrResponse.result);
      });
      return deferred;
    },

    init: function(){

        this.getDataFromServer().done(_.bind(function(resp1, resp2) {

           // Do the operations which you wanted to do when you
           // get a response from Ajax, for example, log response.
        }, this));
    }
};
App.init();

Perché JS non ha modo di sapere che è necessario attendere affinché il foo() termini prima di eseguire $ajax(). In altre parole, non sa che foo() è asincrono - è qualcosa che non produrrà latte fino a qualche ora futura. JS e altri linguaggi dichiarativi eseguono una dichiarazione dopo l'altra senza attendere.

Il classico approccio JS a questo problema, sfruttando il fatto che JS supporta le funzioni come oggetti di prima classe che possono essere passati, è passare una funzione come parametro alla richiesta asincrona, che verrà poi richiamata quando ha completato il suo compito in futuro. Questo è l'approccio "callback". Sembra così:

 
function foo(result) {
    $.ajax({
        url: '...',
        success: function(response) {
            result.response = response;   // Store the async result
        }
    });
}

var result = { response: null };   // Object to hold the async result
foo(result);                       // Returns before the async completes

Inizia il foo(), ordina il latte, quindi, quando e solo quando arriva, invoca result.response.

Il problema con questo approccio callback è che inquina la normale semantica di una funzione che riporta il suo risultato con callback(); invece, le funzioni non devono riportare i risultati chiamando una richiamata fornita come parametro. Inoltre, questo approccio può diventare rapidamente ingombrante quando si ha a che fare con sequenze di eventi più lunghe. Per esempio, diciamo che voglio aspettare che il latte sia messo nel caffè, e solo allora compio un terzo passaggio, cioè bevendo il caffè. Alla fine ho bisogno di scrivere qualcosa del genere:

 foo()

dove sto passando a

var lat = "";
var lon = "";
function callback(data) {
    lat = data.lat;
    lon = data.lon;
}
function getLoc() {
    var url = "http://ip-api.com/json"
    $.getJSON(url, function(data) {
        callback(data);
    });
}

getLoc();
sia il latte da inserire, sia l'azione (
var milk = order_milk();
put_in_coffee(milk);
) da eseguire una volta inserito il latte. Tale codice diventa difficile da scrivere, leggere e eseguire il debug.

In questo caso, potremmo riscrivere il codice nella domanda come:

 order_milk

Inserisci promesse

Questa era la motivazione per la nozione di "promessa", che è un particolare tipo di valore che rappresenta un risultato futuro o asincrono di qualche tipo. Può rappresentare qualcosa che è già accaduto, o che succederà in futuro, o potrebbe non accadere mai. Le promesse hanno un unico metodo, denominato put_in_coffee, al quale si passa un'azione da eseguire quando il risultato che la promessa rappresenta è stato realizzato.

Nel caso del nostro latte e caffè, progettiamo order_milk per restituire una promessa per il latte in arrivo, quindi specificate

order_milk(put_in_coffee);
come azione order_milk, come segue:  put_in_coffee

Un vantaggio di questo è che possiamo metterli insieme per creare sequenze di occorrenze future ("concatenazione"):

 return

Applichiamo le promesse al tuo particolare problema. Comprimeremo la nostra logica di richiesta all'interno di una funzione, che restituisce una promessa:

 
order_milk(function(milk) { put_in_coffee(milk, drink_coffee); }

In realtà, tutto ciò che abbiamo fatto è stato aggiungere un put_in_coffee alla chiamata a drink_coffee. Funziona perché il

var answer;
$.ajax('/foo.json') . done(function(response) {
  callback(response.data);
});

function callback(data) {
  console.log(data);
}
di jQuery restituisce già una sorta di cosa promettente. (In pratica, senza entrare nei dettagli, preferiremmo avvolgere questa chiamata in modo che il ritorno sia una vera promessa, o usare qualche alternativa al then che lo faccia.) Ora, se vogliamo caricare il file e aspettare che finisca e poi fai qualcosa, possiamo semplicemente dire  order_milk

per esempio,

 put_in_coffee

Quando utilizzi le promesse, finiamo per passare molte funzioni in then, quindi è spesso utile utilizzare le funzioni di freccia in stile ES6 più compatte:

 
order_milk() . then(put_in_coffee)

La parola chiave
order_milk() . then(put_in_coffee) . then(drink_coffee)

Ma c'è ancora qualcosa di vagamente insoddisfacente nel dover scrivere il codice in un modo se si tratta di synono e in un modo completamente diverso se asincrono. Per sincrono, scriviamo

 
function get_data() {
  return $.ajax('/foo.json');
}

ma se return è asincrono, con le promesse dobbiamo scrivere

 $.ajax

In alto, abbiamo detto, "JS non ha modo di sapere che è necessario attendere affinché la prima chiamata finisca prima di eseguire il secondo". Non sarebbe bello se fosse un modo per dirlo a JS? Si scopre che esiste la parola chiave $.ajax, utilizzata all'interno di un tipo speciale di funzione chiamata funzione "asincrona". Questa funzione fa parte della versione imminente di ES, ma è già disponibile in transpilers come Babel, dato i preset giusti. Questo ci permette semplicemente di scrivere

 $.ajax

Nel tuo caso, potresti scrivere qualcosa come

 
get_data() . then(do_something)
    
28
2018-12-10 05: 52: 30Z

Naturalmente ci sono molti approcci come la richiesta sincrona, promessa, ma dalla mia esperienza penso che dovresti usare l'approccio callback. È naturale il comportamento asincrono di Javascript. Pertanto, lo snippet di codice può essere riscritto in modo leggermente diverso:

 
get_data() . 
  then(function(data) { console.log(data); });
    
27
2018-03-07 14: 23: 16Z
  1. Non c'è nulla di intrinsecamente asincrono su callback o JavaScript.
    2018-03-18 19: 48: 53Z

La domanda era:

  

Come posso restituire la risposta da una chiamata asincrona?

che può essere interpretato come:

  

Come rendere il codice asincrono aspetto sincrono ?

La soluzione sarà evitare i callback e utilizzare una combinazione di Promises e async /await .

Vorrei fare un esempio per una richiesta Ajax.

(Anche se può essere scritto in Javascript, preferisco scriverlo in Python e compilarlo in Javascript usando Transcrypt . Sarà abbastanza chiaro.)

Prima abilita l'utilizzo di JQuery, per avere then disponibile come

get_data() . 
  then(data => console.log(data));
:  async

Definisci una funzione che restituisce una Promessa , in questo caso una chiamata Ajax:

 
a();
b();

Utilizza il codice asincrono come se fosse sincrono :

 a     
25
2018-05-24 08: 36: 33Z

Uso di Promise

La risposta più perfetta a questa domanda è usare

a() . then(b);
.  await

Uso

 
async function morning_routine() {
  var milk   = await order_milk();
  var coffee = await put_in_coffee(milk);
  await drink(coffee);
}

Ma aspetta ...!

C'è un problema con l'utilizzo delle promesse!

Perché dovremmo utilizzare la nostra Promessa personalizzata?

Stavo usando questa soluzione per un po 'finché non ho capito che c'è un errore nei vecchi browser:

 
async function foo() {
  data = await get_data();
  console.log(data);
}

Quindi ho deciso di implementare la mia classe Promise per i compilatori ES3 al di sotto js se non è definita. Basta aggiungere questo codice prima del codice principale e quindi utilizzare in sicurezza Promise!

 
function foo() {
    var result;

    $.ajax({
        url: '...',
        success: function(response) {
            myCallback(response);
        }
    });

    return result;
}

function myCallback(response) {
    // Does something.
}
    
17
2019-05-28 10: 08: 52Z

Vediamo prima la foresta prima di guardare gli alberi.

Ci sono molte risposte informative con grandi dettagli qui, non ripeterò nessuno di loro. La chiave per programmare in JavaScript è il primo modello mentale corretto dell'esecuzione complessiva.

  1. I punti di accesso vengono eseguiti come risultato di un evento. Per Ad esempio, un tag script con codice viene caricato nel browser. (Di conseguenza, questo è il motivo per cui potrebbe essere necessario preoccuparsi di prontezza della pagina per eseguire il tuo codice se richiede elementi dom da costruire prima, ecc.)
  2. Il tuo codice viene eseguito in completamenton - tuttavia molte chiamate asincrone lo chiamano rende - senza eseguire qualsiasi dei tuoi callback, incluso XHR richieste, imposta timeout, dom handler, ecc. Ognuno di questi callback che aspettano di essere eseguiti si mette in coda, aspettando il loro turno per essere eseguito dopo che gli altri eventi che hanno sparato hanno terminato l'esecuzione.
  3. Ogni singolo richiamo a una richiesta XHR, imposta timeout o dom l'evento una volta richiamato verrà quindi eseguito fino al completamento.

La buona notizia è che se capisci bene questo punto, non dovrai mai preoccuparti delle condizioni di gara. In primo luogo, devi innanzitutto organizzare il tuo codice come essenzialmente la risposta a diversi eventi discreti e come desideri raggrupparli in una sequenza logica. Puoi usare promesse o livelli superiori asincroni /attendere come strumenti a tale scopo, oppure puoi crearne uno tuo.

Ma non dovresti usare alcuno strumento tattico per risolvere un problema finché non ti senti a tuo agio con il vero dominio del problema. Disegna una mappa di queste dipendenze per sapere cosa deve essere eseguito quando. Tentare un approccio ad hoc a tutte queste callback non ti servirà per niente.

    
14
2017-10-29 08: 36: 37Z

Usando ES2017 dovresti avere questa come dichiarazione di funzione

 $

E eseguirlo in questo modo.

 S

O la sintassi Promise

 
__pragma__ ('alias', 'S', '$')
    
14
2018-01-24 06: 18: 55Z
  1. potrebbe quella seconda funzione riutilizzabile ??
    2019-02-17 02: 02: 09Z
def read(url: str):
    deferred = S.Deferred()
    S.ajax({'type': "POST", 'url': url, 'data': { },
        'success': lambda d: deferred.resolve(d),
        'error': lambda e: deferred.reject(e)
    })
    return deferred.promise()
fonte posta Qui