javascript async - Como faço para retornar a resposta de uma chamada assíncrona?





anonymous function (25)


Resposta curta : Seu foo()método retorna imediatamente, enquanto a $ajax()chamada é executada de forma assíncrona após a função retornar . O problema é, então, como ou onde armazenar os resultados recuperados pela chamada assíncrona assim que ela retornar.

Várias soluções foram dadas neste segmento. Talvez a maneira mais fácil seja passar um objeto para o foo()método e armazenar os resultados em um membro desse objeto após a conclusão da chamada assíncrona.

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

Observe que a chamada para foo()ainda retornará nada útil. No entanto, o resultado da chamada assíncrona agora será armazenado em result.response.

Eu tenho uma função foo que faz um pedido Ajax. Como posso retornar a resposta do foo ?

Eu tentei retornar o valor do retorno de chamada de success , bem como atribuir a resposta a uma variável local dentro da função e retorná-la, mas nenhuma dessas maneiras realmente retorna a resposta.

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`.



É um problema muito comum que enfrentamos enquanto lutamos com os 'mistérios' do JavaScript. Deixe-me tentar desmistificar esse mistério hoje.

Vamos começar com uma função JavaScript simples:

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

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

Essa é uma chamada de função síncrona simples (em que cada linha de código é 'finalizada com seu trabalho' antes da próxima em sequência) e o resultado é o mesmo esperado.

Agora vamos adicionar um pouco de torção, introduzindo pouco atraso em nossa função, para que todas as linhas de código não sejam "finalizadas" em sequência. Assim, ele emulará o comportamento assíncrono da função:

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

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

Então lá vai você, esse atraso apenas quebrou a funcionalidade que esperávamos! Mas o que exatamente aconteceu? Bem, é realmente bastante lógico se você olhar o código. a função foo(), após a execução, não retorna nada (assim o valor retornado é undefined), mas inicia um timer, que executa uma função após 1s para retornar 'wohoo'. Mas, como você pode ver, o valor atribuído à barra é o material imediatamente retornado de foo (), e nada mais que vem depois.

Então, como lidamos com essa questão?

Vamos perguntar nossa função para uma promessa . A promessa é realmente sobre o que significa: significa que a função garante que você forneça qualquer saída obtida no futuro. então vamos ver isso em ação para o nosso pequeno problema acima:

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'
});

Assim, o resumo é - para lidar com as funções assíncronas como chamadas baseadas em ajax, etc., você pode usar uma promessa para resolveo valor (que você pretende retornar). Assim, em resumo, você resolve o valor em vez de retornar , em funções assíncronas.

ATUALIZAÇÃO (Promessas com async / await)

Além de usar then/catchpara trabalhar com promessas, existe mais uma abordagem. A ideia é reconhecer uma função assíncrona e aguardar a resolução das promessas , antes de passar para a próxima linha de código. Ainda é apenas o promisessob o capô, mas com uma abordagem sintática diferente. Para tornar as coisas mais claras, você pode encontrar uma comparação abaixo:

então / captura a versão:

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

versão async / await:

  async function fetchUsers(){
     try{
        let users = await getUsers()
        return users;
     }
     catch(err){
        throw err;
     }
  }



Js é um único encadeado.

O navegador pode ser dividido em três partes:

1) Evento Loop

2) API da Web

3) Fila de Eventos

Event Loop é executado para sempre, ou seja, tipo de loop infinito.Event Queue é onde todas as suas funções são empurradas em algum evento (exemplo: click) este é um por um realizado fora da fila e colocado em Event loop que executa esta função e se prepara para o próximo, após o primeiro ser executado. Isso significa que a execução de uma função não é iniciada até que a função antes da fila seja executada no loop de eventos.

Agora vamos pensar que nós empurramos duas funções em uma fila, uma é para obter um dado do servidor e outra utiliza esse dado. Nós pressionamos a função serverRequest () na fila primeiro e depois a função utiliseData (). A função serverRequest entra no loop de eventos e faz uma chamada ao servidor, pois nunca sabemos quanto tempo será necessário para obter dados do servidor, portanto, é esperado que esse processo leve tempo e, por isso, estamos ocupando nosso loop de eventos. A API entra em função, toma essa função do loop de eventos e lida com o servidor tornando o loop de eventos livre para que possamos executar a próxima função da fila.A próxima função na fila é utiliseData () que entra em loop, mas sem dados disponíveis desperdício e execução da próxima função continua até o final da fila (isso é chamado de chamada assíncrona, ou seja, podemos fazer outra coisa até obtermos dados)

Vamos supor que nossa função serverRequest () tenha uma instrução de retorno em um código, quando recebermos de volta os dados da API da Web do servidor, ele os colocará na fila no final da fila. À medida que é empurrado no final da fila, não podemos utilizar seus dados, pois não há mais nenhuma função em nossa fila para utilizar esses dados. Assim, não é possível retornar algo da chamada assíncrona.

Assim, a solução para isso é callback ou promessa .

Uma imagem de uma das respostas aqui, explica corretamente o uso do retorno de chamada ... Damos à nossa função (função utilizando dados retornados do servidor) a função de servidor de chamada.

 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);

}

No meu código é chamado como

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");
}

Leia aqui para novos métodos no ECMA (2016/17) para fazer chamadas assíncronas (@Felix Kling Answer on Top) https://.com/a/14220323/7579856




Você pode usar essa biblioteca personalizada (escrita usando o Promise) para fazer uma chamada remota.

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);
        };
    });
}

Exemplo de uso simples:

$http({
    method: 'get',
    url: 'google.com'
}).then(function(response) {
    console.log(response);
}, function(error) {
    console.log(error)
});



Aqui estão algumas abordagens para trabalhar com solicitações assíncronas:

  1. then()
  2. Q - Uma biblioteca de promessas para JavaScript
  3. A + Promises.js
  4. jQuery adiada
  5. API XMLHttpRequest
  6. Usando o conceito de retorno de chamada - como implementação na primeira resposta

Exemplo: jQuery adiou a implementação para trabalhar com várias solicitações

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();




Outra abordagem para retornar um valor de uma função assíncrona é passar em um objeto que armazenará o resultado da função assíncrona.

Aqui está um exemplo do mesmo:

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');
});

Eu estou usando o resultobjeto para armazenar o valor durante a operação assíncrona. Isso permite que o resultado esteja disponível mesmo após o trabalho assíncrono.

Eu uso muito essa abordagem. Eu estaria interessado em saber o quão bem esta abordagem funciona onde a ligação do resultado através de módulos consecutivos está envolvida.




É claro que existem muitas abordagens como solicitação síncrona, promessa, mas pela minha experiência eu acho que você deveria usar a abordagem de retorno de chamada. É natural o comportamento assíncrono do Javascript. Então, seu snippet de código pode ser reescrito um pouco diferente:

function foo() {
    var result;

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

    return result;
}

function myCallback(response) {
    // Does something.
}



XMLHttpRequest 2 (primeiro leia as respostas de Benjamin Gruenbaum e Felix Kling)

Se você não usa o jQuery e quer um pequeno XMLHttpRequest 2 que funcione nos navegadores modernos e também nos navegadores móveis, sugiro usá-lo desta maneira:

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

Como você pode ver:

  1. É mais curto que todas as outras funções listadas.
  2. O retorno de chamada é definido diretamente (portanto, nenhum fechamento extra desnecessário).
  3. Ele usa o novo onload (para que você não tenha que verificar o estado do readyState &&)
  4. Existem algumas outras situações que não me lembro que tornam o XMLHttpRequest 1 chato.

Existem duas maneiras de obter a resposta desta chamada Ajax (três usando o nome var XMLHttpRequest):

O mais simples:

this.response

Ou se por algum motivo você bind() o retorno de chamada a uma classe:

e.target.response

Exemplo:

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

Ou (o acima é melhor funções anônimas são sempre um problema):

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

Nada mais fácil.

Agora, algumas pessoas provavelmente dirão que é melhor usar onreadystatechange ou até mesmo o nome da variável XMLHttpRequest. Isto é errado.

Confira os recursos avançados do XMLHttpRequest

Ele suportava todos os navegadores modernos. E eu posso confirmar como estou usando essa abordagem desde XMLHttpRequest 2 existe. Eu nunca tive nenhum tipo de problema em todos os navegadores que uso.

onreadystatechange só é útil se você quiser obter os cabeçalhos no estado 2.

Usar o nome da variável XMLHttpRequest é outro grande erro, já que você precisa executar o callback dentro dos closures onload / oreadystatechange, senão você o perdeu.

Agora, se você quiser algo mais complexo usando post e FormData, você pode facilmente estender essa função:

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)
}

Mais uma vez ... é uma função muito curta, mas obtém e publica.

Exemplos de uso:

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

Ou passe um elemento de formulário completo ( document.getElementsByTagName('form')[0] ):

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

Ou defina alguns valores personalizados:

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

Como você pode ver, eu não implantei a sincronização ... é uma coisa ruim.

Tendo dito isso ... por que não fazer isso da maneira mais fácil?

Como mencionado no comentário, o uso de error & synchronous rompe completamente o ponto da resposta. Qual é um bom caminho curto para usar o Ajax da maneira correta?

Manipulador de erro

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);

No script acima, você tem um manipulador de erros que é definido estaticamente para não comprometer a função. O manipulador de erros também pode ser usado para outras funções.

Mas, para realmente obter um erro, a única maneira é escrever uma URL errada, nesse caso, cada navegador gera um erro.

Manipuladores de erros talvez sejam úteis se você definir cabeçalhos personalizados, definir o tipo de resposta como buffer de matriz de blob ou o que for ....

Mesmo se você passar 'POSTAPAPAP' como o método, não lançará um erro.

Mesmo se você passar 'fdggdgilfdghfldj' como formdata, isso não causará um erro.

No primeiro caso, o erro está dentro do displayAjax() sob this.statusText como Method not Allowed .

No segundo caso, simplesmente funciona. Você tem que verificar no lado do servidor, se você passou os dados do post direito.

domínio cruzado não permitido gera erro automaticamente.

Na resposta de erro, não há códigos de erro.

Existe apenas o this.type que está configurado para erro.

Por que adicionar um manipulador de erros se você não tem controle sobre erros? A maioria dos erros são retornados dentro desta função de retorno de chamada displayAjax() .

Portanto, não há necessidade de verificações de erros se você conseguir copiar e colar o URL corretamente. ;)

PS: Como o primeiro teste eu escrevi x ('x', displayAjax) ..., e ele obteve uma resposta totalmente ... ??? Então, verifiquei a pasta onde o HTML está localizado e havia um arquivo chamado 'x.xml'. Então, mesmo se você esquecer a extensão do seu arquivo XMLHttpRequest 2 irá encontrá-lo . Eu LOL'd

Leia um arquivo síncrono

Não faça isso.

Se você quiser bloquear o navegador por um tempo, carregue um arquivo txt grande e agradável síncrono.

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

Agora você pode fazer

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

Não há outra maneira de fazer isso de maneira não assíncrona. (Sim, com loop setTimeout ... mas sério?)

Outro ponto é ... se você trabalha com APIs ou apenas você possui arquivos de lista ou o que você sempre usa diferentes funções para cada requisição ...

Só se você tiver uma página onde você carrega sempre o mesmo XML / JSON ou o que você precisa apenas uma função. Nesse caso, modifique um pouco a função Ajax e substitua b por sua função especial.

As funções acima são para uso básico.

Se você quiser estender a função ...

Sim você pode.

Estou usando muitas APIs e uma das primeiras funções que integro em cada página HTML é a primeira função Ajax nessa resposta, com GET apenas ...

Mas você pode fazer muitas coisas com XMLHttpRequest 2:

Eu fiz um gerenciador de downloads (usando intervalos em ambos os lados com currículo, filereader, sistema de arquivos), vários conversores resizers imagem usando canvas, preencher bancos de dados websql com base64images e muito mais ... Mas nestes casos você deve criar uma função apenas para esse fim ... às vezes você precisa de um blob, buffers de matriz, você pode definir cabeçalhos, substituir o tipo MIME e há muito mais ...

Mas a questão aqui é como retornar uma resposta Ajax ... (acrescentei uma maneira fácil).




Enquanto promessas e retornos de chamada funcionam bem em muitas situações, é uma dor na parte traseira para expressar algo como:

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

Você acabaria passando async1; verifique se nameestá indefinido ou não e chame o callback de acordo.

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

async1(name, async2)

Enquanto está tudo bem em pequenos exemplos, fica irritante quando você tem muitos casos similares e manipulação de erros envolvidos.

Fibers ajuda na solução do problema.

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
}

Você pode verificar o projeto here .




Angular1

Para as pessoas que estão usando o AngularJS , pode lidar com essa situação usando Promises.

Here diz,

As promessas podem ser usadas para desestimular funções assíncronas e permitir a cadeia de várias funções juntas.

Você pode encontrar uma boa explicação here também.

Exemplo encontrado nos docs mencionados abaixo.

  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.

Angular2 e mais tarde

Em Angular2com olhar o seguinte exemplo, mas é recommended usar Observablescom Angular2.

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

}

Você pode consumir isso dessa maneira,

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

Veja o post original aqui. Mas o Typescript não suporta promessas nativas , se você quiser usá-lo, você pode precisar de um plugin para isso.

Além disso, aqui estão as promessas spec aqui.




Dê uma olhada neste exemplo:

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);
    });
});

Como você pode ver, getJokeestá retornando uma promessa resolvida (ela é resolvida ao retornar res.data.value). Portanto, você aguarda até que a solicitação $ http.get seja concluída e, em seguida, console.log (res.joke) seja executado (como um fluxo assíncrono normal).

Este é o plnkr:

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




Usando ES2017 você deve ter isso como a declaração de função

async function foo() {
    var response = await $.ajax({url: '...'})
    return response;
}

E executá-lo assim.

(async function() {
    try {
        var result = await foo()
        console.log(result)
    } catch (e) {}
})()

Ou a sintaxe do Promise

foo().then(response => {
    console.log(response)

}).catch(error => {
    console.log(error)

})



Em vez de jogar código em você, existem dois conceitos que são fundamentais para entender como o JS lida com retornos de chamada e assincronia. (Isso é mesmo uma palavra?)

O Loop de Eventos e o Modelo de Concorrência

Há três coisas que você precisa estar ciente; A fila; o loop de eventos e a pilha

Em termos gerais e simplistas, o loop de eventos é como o gerente de projeto, ele está constantemente ouvindo qualquer função que queira executar e se comunique entre a fila e a pilha.

while (queue.waitForMessage()) {
   queue.processNextMessage();
}

Depois de receber uma mensagem para executar algo, ela é adicionada à fila. A fila é a lista de coisas que estão aguardando para serem executadas (como sua solicitação AJAX). imagine assim:

 1. call foo.com/api/bar using foobarFunc
 2. Go perform an infinite loop
 ... and so on

Quando uma dessas mensagens for executada, ela exibe a mensagem da fila e cria uma pilha, a pilha é tudo que JS precisa executar para executar a instrução na mensagem. Então, no nosso exemplo, está sendo dito para ligarfoobarFunc

function foobarFunc (var) {
  console.log(anotherFunction(var));
}

Então, qualquer coisa que o foobarFunc precise executar (no nosso caso anotherFunction) será colocada na pilha. executado e, em seguida, esquecido - o loop de eventos passará para a próxima coisa na fila (ou para ouvir mensagens)

A principal coisa aqui é a ordem de execução. Isso é

Quando algo vai correr

Quando você faz uma chamada usando AJAX para uma parte externa ou executa qualquer código assíncrono (um setTimeout, por exemplo), o Javascript depende de uma resposta antes que ela possa prosseguir.

A grande questão é quando chegará a resposta? A resposta é que não sabemos - então o loop de eventos está esperando que a mensagem diga "hey run me". Se o JS apenas esperou por essa mensagem de forma síncrona, o seu aplicativo congelaria e seria uma droga. Então, o JS continua executando o próximo item na fila enquanto aguarda a mensagem ser adicionada de volta à fila.

É por isso que com a funcionalidade assíncrona usamos coisas chamadas de callbacks . É como uma then() literalmente. Como em prometo retornar algo em algum momento, o jQuery usa callbacks específicos chamados deffered.done deffered.faile deffered.always(entre outros). Você pode vê-los todos here

Então, o que você precisa fazer é passar uma função que é prometida para executar em algum momento com dados que são passados ​​para ele.

Como um retorno de chamada não é executado imediatamente, mas posteriormente, é importante passar a referência para a função não executada. assim

function foo(bla) {
  console.log(bla)
}

por isso na maioria das vezes (mas nem sempre) você vai passar foonãofoo()

Espero que isso faça algum sentido. Quando você encontra coisas como essas que parecem confusas - eu recomendo que você leia a documentação totalmente para, pelo menos, ter uma compreensão dela. Isso fará de você um desenvolvedor muito melhor.




A questão era:

Como faço para retornar a resposta de uma chamada assíncrona?

que PODE ser interpretado como:

Como fazer com que o código assíncrono pareça síncrono ?

A solução será evitar retornos de chamada e usar uma combinação de Promessas e async / aguardar .

Eu gostaria de dar um exemplo para um pedido de Ajax.

(Embora possa ser escrito em Javascript, eu prefiro escrevê-lo em Python e compilá-lo para JavaScript usando o Transcrypt . Ele ficará claro o suficiente.)

Vamos primeiro habilitar o uso do JQuery, para ter $disponível como S:

__pragma__ ('alias', 'S', '$')

Defina uma função que retorne um Promise , neste caso uma chamada Ajax:

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()

Use o código assíncrono como se fosse síncrono :

async def readALot():
    try:
        result1 = await read("url_1")
        result2 = await read("url_2")
    except Exception:
        console.warn("Reading a lot failed")



Nos encontramos em um universo que parece progredir em uma dimensão que chamamos de "tempo". Nós realmente não entendemos o que é o tempo, mas desenvolvemos abstrações e vocabulário que nos permitem raciocinar e falar sobre isso: "passado", "presente", "futuro", "antes", "depois".

Os sistemas computacionais que construímos - cada vez mais - têm o tempo como uma dimensão importante. Certas coisas estão prontas para acontecer no futuro. Então, outras coisas precisam acontecer depois que as primeiras coisas ocorrerem. Essa é a noção básica chamada "assincronia". Em nosso mundo cada vez mais conectado, o caso mais comum de assincronicidade é esperar que algum sistema remoto responda a alguma solicitação.

Considere um exemplo. Você chama o leiteiro e pede um pouco de leite. Quando chega, você quer colocá-lo em seu café. Você não pode colocar o leite no seu café agora, porque ainda não está aqui. Você tem que esperar que venha antes de colocá-lo em seu café. Em outras palavras, o seguinte não funcionará:

var milk = order_milk();
put_in_coffee(milk);

Porque o JS não tem como saber que ele precisa aguardar para order_milkser concluído antes de ser executado put_in_coffee. Em outras palavras, ele não sabe que order_milké assíncrono - é algo que não vai resultar em leite até algum tempo futuro. JS e outras linguagens declarativas, executam uma instrução após a outra sem esperar.

A abordagem clássica do JS a esse problema, aproveitando o fato de que o JS suporta funções como objetos de primeira classe que podem ser passados, é passar uma função como um parâmetro para o pedido assíncrono, que ele invocará quando tiver concluído sua tarefa em algum momento no futuro. Essa é a abordagem de "retorno de chamada". Se parece com isso:

order_milk(put_in_coffee);

order_milkarranca, ordena o leite, então, quando e somente quando chega, invoca put_in_coffee.

O problema com essa abordagem de retorno de chamada é que ela polui a semântica normal de uma função relatando seu resultado com return; em vez disso, as funções não precisam reportar seus resultados chamando um retorno de chamada fornecido como um parâmetro. Além disso, essa abordagem pode rapidamente tornar-se pesada quando se lida com seqüências mais longas de eventos. Por exemplo, digamos que eu queira esperar que o leite seja colocado no café e, então, e só então, dar um terceiro passo, ou seja, beber o café. Eu acabo precisando escrever algo assim:

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

onde eu estou passando para put_in_coffeeo leite para colocar nele, e também a action ( drink_coffee) para executar uma vez que o leite foi colocado. Tal código torna-se difícil de escrever, ler e depurar.

Nesse caso, poderíamos reescrever o código na pergunta como:

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

function callback(data) {
  console.log(data);
}

Digite promessas

Essa foi a motivação para a noção de uma "promessa", que é um tipo particular de valor que representa um resultado futuro ou assíncrono de algum tipo. Pode representar algo que já aconteceu, ou que vai acontecer no futuro, ou pode nunca acontecer. As promessas têm um único método, chamado then, para o qual você passa uma ação a ser executada quando o resultado que a promessa representa foi realizado.

No caso do nosso leite e café, nós planejamos order_milkretornar uma promessa para o leite que chega, então especifique put_in_coffeecomo uma thenação, como segue:

order_milk() . then(put_in_coffee)

Uma vantagem disso é que podemos agrupá-las para criar sequências de ocorrências futuras ("encadeamento"):

order_milk() . then(put_in_coffee) . then(drink_coffee)

Vamos aplicar promessas ao seu problema particular. Vamos envolver nossa lógica de solicitação dentro de uma função, que retorna uma promessa:

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

Na verdade, tudo o que fizemos foi adicionado returnà chamada para $.ajax. Isso funciona porque o jQuery $.ajaxjá retorna uma espécie de promessa. (Na prática, sem entrar em detalhes, preferimos encerrar essa chamada para retornar uma promessa real ou usar alguma alternativa para $.ajaxisso.) Agora, se quisermos carregar o arquivo e esperar que ele termine e, em seguida, fazer alguma coisa, podemos simplesmente dizer

get_data() . then(do_something)

por exemplo,

get_data() . 
  then(function(data) { console.log(data); });

Ao usar promessas, acabamos passando muitas funções then, então é sempre útil usar as funções de seta mais compactas no estilo ES6:

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

A asyncpalavra-chave

Mas ainda há algo vagamente insatisfatório em ter que escrever código de uma maneira síncrona e um modo bem diferente se assíncrono. Para síncrono, escrevemos

a();
b();

mas se aé assíncrono, com promessas, temos que escrever

a() . then(b);

Acima, dissemos "JS não tem como saber que ele precisa aguardar a primeira chamada terminar antes de executar a segunda". Não seria bom se foi alguma maneira de dizer JS isso? Acontece que existe - a awaitpalavra - chave, usada dentro de um tipo especial de função chamada de função "assíncrona". Este recurso faz parte da próxima versão do ES, mas já está disponível em transpilers, como o Babel, que possui as predefinições corretas. Isso nos permite simplesmente escrever

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

No seu caso, você seria capaz de escrever algo como

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



Resposta curta é, você tem que implementar um retorno de chamada como este:

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

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



→ Para uma explicação mais geral do comportamento assíncrono com exemplos diferentes, consulte Por que minha variável é inalterada depois que eu a modifico dentro de uma função? - Referência de código assíncrona

→ Se você já entendeu o problema, pule para as possíveis soluções abaixo.

O problema

O A em Ajax significa asynchronous . Isso significa que enviar a solicitação (ou receber a resposta) é retirado do fluxo de execução normal. No seu exemplo, $.ajax retorna imediatamente e a próxima instrução return result; , é executado antes que a função que você passou como callback de success seja chamada.

Aqui está uma analogia que, esperamos, faz a diferença entre o fluxo síncrono e assíncrono mais claro:

Síncrono

Imagine que você faz uma ligação para um amigo e pede que ele procure algo para você. Embora possa demorar um pouco, você espera no telefone e olha para o espaço, até que seu amigo lhe dê a resposta que você precisa.

O mesmo acontece quando você faz uma chamada de função contendo código "normal":

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

var item = findItem();

// Do something with item
doSomethingElse();

Mesmo que findItem possa levar muito tempo para executar, qualquer código vindo depois de var item = findItem(); tem que esperar até que a função retorne o resultado.

Assíncrono

Você liga para seu amigo novamente pelo mesmo motivo. Mas desta vez você diz a ele que está com pressa e ele deve ligar de volta para o seu celular. Você desliga, sai de casa e faz o que você planejou fazer. Depois que seu amigo ligar de volta, você está lidando com as informações que ele lhe deu.

É exatamente o que está acontecendo quando você faz uma requisição Ajax.

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

Em vez de aguardar a resposta, a execução continua imediatamente e a instrução após a execução da chamada Ajax é executada. Para obter a resposta eventualmente, você fornece uma função a ser chamada assim que a resposta for recebida, um retorno de chamada (note algo? Chamar de volta ?). Qualquer declaração que venha depois dessa chamada é executada antes do callback ser chamado.

Solução (s)

Abrace a natureza assíncrona do JavaScript! Embora determinadas operações assíncronas forneçam contrapartes síncronas (assim como o "Ajax"), geralmente é desencorajado usá-las, especialmente em um contexto de navegador.

Por que é ruim você pergunta?

O JavaScript é executado no encadeamento de interface do usuário do navegador e qualquer processo de execução longa bloqueará a interface do usuário, deixando-a sem resposta. Além disso, há um limite superior no tempo de execução para o JavaScript e o navegador perguntará ao usuário se deve continuar a execução ou não.

Tudo isso é uma experiência ruim para o usuário. O usuário não será capaz de dizer se tudo está funcionando bem ou não. Além disso, o efeito será pior para usuários com uma conexão lenta.

A seguir, veremos três soluções diferentes que estão sendo construídas umas sobre as outras:

  • Promessas com async/await await (ES2017 +, disponível em navegadores mais antigos se você usar um transpilador ou regenerador)
  • Retornos de chamada (popular no nó)
  • Promessas com then() (ES2015 +, disponível em navegadores mais antigos se você usar uma das muitas bibliotecas prometidas)

Todos os três estão disponíveis nos navegadores atuais e no nó 7+.

ES2017 +: Promessas com async/await await

A versão do ECMAScript lançada em 2017 introduziu suporte em nível de sintaxe para funções assíncronas. Com a ajuda de async e await , você pode escrever assíncrono em um "estilo síncrono". O código ainda é assíncrono, mas é mais fácil de entender.

async/await baseia-se em promessas: uma função async sempre retorna uma promessa. await "desembrulha" uma promessa e resulta no valor com o qual a promessa foi resolvida ou lança um erro se a promessa foi rejeitada.

Importante: Você só pode usar o await dentro de uma função async . Isso significa que, no nível mais alto, você ainda precisa trabalhar diretamente com a promessa.

Você pode ler mais sobre async/await e await no MDN.

Aqui está um exemplo que se baseia no atraso acima:

// 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;
  }
}

// Async functions always return a promise
getAllBooks()
  .then(function(books) {
    console.log(books);
  });

As versões atuais do browser e do node suportam async/await . Você também pode suportar ambientes mais antigos, transformando seu código em ES5 com a ajuda do regenerator (ou ferramentas que usam o regenerador, como o Babel ).

Deixe funções aceitar retornos de chamada

Um retorno de chamada é simplesmente uma função passada para outra função. Essa outra função pode chamar a função passada sempre que estiver pronta. No contexto de um processo assíncrono, o retorno de chamada será chamado sempre que o processo assíncrono for concluído. Normalmente, o resultado é passado para o retorno de chamada.

No exemplo da pergunta, você pode fazer o foo aceitar um retorno de chamada e usá-lo como callback de success . Então, é isso

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

torna-se

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

Aqui nós definimos a função "inline", mas você pode passar qualquer referência de função:

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

foo(myCallback);

foo si é definido da seguinte forma:

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

callback se referirá à função que passamos para foo quando o chamamos e simplesmente passamos para o success . Ou seja, uma vez que a requisição Ajax é bem sucedida, $.ajax irá chamar o callback e passar a resposta para o callback (que pode ser referido com o result , pois é assim que definimos o callback).

Você também pode processar a resposta antes de transmiti-la ao retorno de chamada:

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

É mais fácil escrever código usando callbacks do que parece. Afinal, o JavaScript no navegador é fortemente orientado a eventos (eventos DOM). Receber a resposta do Ajax não é mais do que um evento.
Dificuldades podem surgir quando você tem que trabalhar com código de terceiros, mas a maioria dos problemas pode ser resolvida apenas pensando no fluxo do aplicativo.

ES2015 +: Promessas com then()

A then() é um novo recurso do ECMAScript 6 (ES2015), mas já possui um bom suporte ao navegador . Há também muitas bibliotecas que implementam a API Promises padrão e fornecem métodos adicionais para facilitar o uso e a composição de funções assíncronas (por exemplo, bluebird ).

Promessas são contêineres para valores futuros . Quando a promessa recebe o valor (é resolvido ) ou quando é cancelado ( rejeitado ), ele notifica todos os seus "ouvintes" que desejam acessar este valor.

A vantagem sobre os retornos de chamadas simples é que eles permitem dissociar seu código e são mais fáceis de compor.

Aqui está um exemplo simples de usar uma 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).
  });

Aplicado à nossa chamada Ajax, poderíamos usar promessas como esta:

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
  });

Descrever todas as vantagens que prometem oferecer está além do escopo desta resposta, mas se você escrever um novo código, você deve considerá-las seriamente. Eles fornecem uma grande abstração e separação do seu código.

Mais informações sobre promessas: HTML5 rocks - JavaScript Promises

Nota lateral: objetos adiados do jQuery

Objetos adiados são a implementação personalizada de promessas do jQuery (antes da padronização da API do Promise). Eles se comportam quase como promessas, mas expõem uma API ligeiramente diferente.

Todo método Ajax do jQuery já retorna um "objeto adiado" (na verdade, uma promessa de um objeto adiado) que você pode simplesmente retornar da sua função:

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

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

Nota lateral: Pegadinhas de promessa

Tenha em mente que promessas e objetos adiados são apenas contêineres para um valor futuro, eles não são o valor em si. Por exemplo, suponha que você tenha o seguinte:

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
}

Este código não compreende os problemas de assincronia acima. Especificamente, $.ajax() não congela o código enquanto ele verifica a página '/ password' no seu servidor - ele envia uma requisição para o servidor e enquanto espera, imediatamente retorna um objeto jQuery Ajax Adiado, não a resposta de o servidor. Isso significa que a instrução if sempre obterá esse objeto Deferred, tratá-lo como true e proceder como se o usuário estivesse logado. Não é bom.

Mas a correção é fácil:

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
});

Não recomendado: chamadas "Ajax" síncronas

Como mencionei, algumas (!) Operações assíncronas têm contrapartes síncronas. Eu não defendo o uso deles, mas por questões de integridade, aqui está como você executaria uma chamada síncrona:

Sem jQuery

Se você usar diretamente um objeto XMLHTTPRequest , passe false como terceiro argumento para .open .

jQuery

Se você usar o jQuery , poderá definir a opção async como false . Observe que esta opção está obsoleta desde o jQuery 1.8. Você pode então usar um callback de success ou acessar a propriedade responseText do objeto jqXHR :

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

Se você usar qualquer outro método do jQuery Ajax, como $.get , $.getJSON , etc., você terá que alterá-lo para $.ajax (já que você só pode passar parâmetros de configuração para $.ajax ).

Atenção! Não é possível fazer uma solicitação JSONP síncrona. O JSONP, por sua própria natureza, é sempre assíncrono (mais uma razão para não considerar essa opção).




Esse é um dos lugares em que duas formas de vinculação de dados usadas em muitos novos frameworks JavaScript funcionarão muito para você ...

Então, se você estiver usando Angular, React ou qualquer outra estrutura que faça duas formas de vinculação de dados, esse problema é simplesmente corrigido para você, portanto, em palavras fáceis, seu resultado está undefinedno primeiro estágio, assim você tem result = undefinedantes de receber os dados então, assim que você obtiver o resultado, ele será atualizado e atribuído ao novo valor que responde à sua chamada Ajax ...

Mas como você pode fazê-lo em javascript puro ou jQuery, por exemplo, como você pediu nesta pergunta?

Você pode usar um retorno de chamada , promessa e recentemente observável para lidar com isso para você, por exemplo em promessas que temos alguma função como success () ou then () que será executada quando seus dados estiverem prontos para você, mesmo com callback ou função de assinatura em observável .

Por exemplo, no seu caso em que você está usando o jQuery , você pode fazer algo assim:

$(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
});

Para mais informações estudo sobre promessas e observables que são maneiras mais recentes de fazer este material assíncrono.




Vamos ver a floresta antes de olhar para as árvores.

Há muitas respostas informativas com grandes detalhes aqui, não vou repetir nenhuma delas. A chave para programar em JavaScript é ter primeiro o modelo mental correto de execução geral.

  1. Seu ponto de entrada é executado como resultado de um evento. Por exemplo, uma tag de script com código é carregada no navegador. (Da mesma forma, é por isso que você pode precisar se preocupar com a prontidão da página para executar seu código se ele requer que os elementos dom sejam construídos primeiro, etc.)
  2. Seu código é executado até a conclusão - não importa quantas chamadas assíncronas ele faça - sem executar nenhum dos seus retornos de chamada, incluindo solicitações XHR, definir tempos limite, manipuladores de eventos, etc. Cada um desses retornos esperando para ser executado ficará em uma fila, esperando sua vez de ser executado após outros eventos que tenham sido executados.
  3. Cada retorno de chamada individual para uma solicitação XHR, definir o tempo limite ou dom o evento, uma vez invocado, será executado até a conclusão.

A boa notícia é que, se você entender bem esse ponto, nunca terá que se preocupar com as condições da corrida. Em primeiro lugar, você deve pensar em como deseja organizar seu código como essencialmente a resposta a diferentes eventos distintos e como agrupá-los em uma sequência lógica. Você pode usar promessas ou novas ferramentas de assíncrono / espera de nível superior para esse fim, ou você pode fazer o seu próprio.

Mas você não deve usar nenhuma ferramenta tática para resolver um problema até se sentir confortável com o domínio real do problema. Desenhe um mapa dessas dependências para saber o que precisa ser executado quando. Tentar uma abordagem ad-hoc para todos esses retornos de chamada simplesmente não vai atendê-lo bem.




O ECMAScript 6 possui 'geradores' que permitem programar facilmente em um estilo assíncrono.

function* myGenerator() {
    const callback = yield;
    let [response] = yield $.ajax("https://.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();
    }
}

Para executar o código acima, faça isso:

const gen = myGenerator(); // Create generator
gen.next(); // Start it
gen.next((...args) => gen.next([...args])); // Set its callback function

Se você precisa direcionar navegadores que não suportam o ES6, você pode executar o código através do Babel ou do compilador de fechamento para gerar o ECMAScript 5.

O retorno de chamada ...argsé empacotado em uma matriz e desestruturado quando você os lê para que o padrão possa lidar com retornos de chamada que possuem vários argumentos. Por exemplo, com o nó fs :

const [err, data] = yield fs.readFile(filePath, "utf-8", callback);



O exemplo a seguir que escrevi mostra como

  • Manipular chamadas HTTP assíncronas;
  • Aguarde a resposta de cada chamada da API;
  • Use o padrão Promise ;
  • Use Promise.All padrão para participar de várias chamadas HTTP;

Este exemplo de trabalho é autônomo. Ele definirá um objeto de solicitação simples que usa o XMLHttpRequestobjeto de janela para fazer chamadas. Ele definirá uma função simples para aguardar a conclusão de várias promessas.

Contexto. O exemplo é consultar o ponto final da API do Spotify Web para procurar playlistobjetos por um determinado conjunto de sequências de consulta:

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

Para cada item, um novo Promise disparará um bloco - ExecutionBlock, analisará o resultado, agendará um novo conjunto de promessas com base no array de resultados, que é uma lista de userobjetos Spotify e executará a nova chamada HTTP de ExecutionProfileBlockforma assíncrona.

Em seguida, você pode ver uma estrutura Promise aninhada, que permite gerar chamadas HTTP aninhadas múltiplas e completamente assíncronas e unir os resultados de cada subconjunto de chamadas Promise.all.

OBSERVAÇÃO As recentes searchAPIs do Spotify exigirão que um token de acesso seja especificado nos cabeçalhos da solicitação:

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

Assim, para executar o exemplo a seguir, você precisa colocar seu token de acesso nos cabeçalhos de solicitação:

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" />

Eu tenho discutido extensivamente essa solução here .




Vou responder com uma história em quadrinhos desenhada à mão horrível. A segunda imagem é a razão pela qual resultestá undefinedno seu exemplo de código.




A solução mais simples é criar uma função JavaScript e chamá-la para o successretorno de chamada do Ajax .

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);    
}); 



Outra solução é executar código via executor sequencial nsynjs .

Se a função subjacente é promisified

O nsynjs avaliará todas as promessas seqüencialmente e colocará o resultado da promessa em datapropriedade:

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>

Se a função subjacente não é promisificada

Etapa 1. Envolva a função com retorno de chamada no wrapper nsynjs-aware (se tiver uma versão promissificada, você poderá ignorar esse teste):

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;

Etapa 2. Coloque a lógica síncrona em função:

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

Etapa 3. Execute a função de maneira síncrona via nnsynjs:

nsynjs.run(process,this,function () {
    console.log("synchronous function finished");
});

Nsynjs avaliará todos os operadores e expressões passo-a-passo, pausando a execução caso o resultado de alguma função lenta não esteja pronto.

Mais exemplos aqui: https://github.com/amaksr/nsynjs/tree/master/examples




Array.prototype.remByVal = function(val) {
    for (var i = 0; i < this.length; i++) {
        if (this[i] === val) {
            this.splice(i, 1);
            i--;
        }
    }
    return this;
}
//Call like
[1, 2, 3, 4].remByVal(3);

Array.prototype.remByVal = function(val) {
    for (var i = 0; i < this.length; i++) {
        if (this[i] === val) {
            this.splice(i, 1);
            i--;
        }
    }
    return this;
}

var rooms = ['hello', 'something']

rooms = rooms.remByVal('hello')

console.log(rooms)





javascript ajax asynchronous ecmascript-6 ecmascript-2017