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





15 Answers

Se você não está usando o jQuery no seu código, esta resposta é para você

Seu código deve ser algo ao longo das linhas deste:

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 fez um ótimo trabalho escrevendo uma resposta para pessoas que usam jQuery para AJAX, e decidi oferecer uma alternativa para pessoas que não são.

( Observe que, para aqueles que usam a nova API de fetch , Angular ou promessas, adicionamos outra resposta abaixo )

O que você está enfrentando

Este é um breve resumo de "Explicação do problema" da outra resposta, se você não tiver certeza depois de ler isto, leia isso.

O A em AJAX significa assíncrono . Isso significa que enviar a solicitação (ou receber a resposta) é retirado do fluxo de execução normal. No seu exemplo, .send 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.

Isso significa que, quando você retornar, o ouvinte que você definiu ainda não foi executado, o que significa que o valor que você está retornando não foi definido.

Aqui está uma analogia simples

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

(Fiddle)

O valor de a retornado é undefined já que a parte a=5 ainda não foi executada. O AJAX age assim, você está retornando o valor antes que o servidor tenha a chance de dizer ao seu navegador qual é esse valor.

Uma solução possível para esse problema é codificar reativamente , informando ao seu programa o que fazer quando o cálculo for concluído.

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

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

Isso é chamado CPS . Basicamente, estamos passando getFive uma ação para executar quando estiver concluída, estamos dizendo ao nosso código como reagir quando um evento é concluído (como nossa chamada AJAX ou, neste caso, o tempo limite).

Uso seria:

getFive(onComplete);

Qual deve alertar "5" para a tela. (Fiddle)

Soluções possíveis

Existem basicamente duas maneiras de resolver isso:

  1. Faça a chamada AJAX síncrona (vamos chamá-lo de SJAX).
  2. Reestruture seu código para funcionar corretamente com retornos de chamada.

1. AJAX Síncrono - Não faça isso !!

Quanto ao AJAX síncrono, não faça isso! A resposta de Felix levanta alguns argumentos convincentes sobre por que é uma má ideia. Para resumir, ele congelará o navegador do usuário até que o servidor retorne a resposta e crie uma experiência de usuário muito ruim. Aqui está outro pequeno resumo do MDN sobre o porquê:

XMLHttpRequest suporta comunicações síncronas e assíncronas. Em geral, no entanto, as solicitações assíncronas devem ser preferenciais às solicitações síncronas por motivos de desempenho.

Em suma, as solicitações síncronas bloqueiam a execução do código ... ... isso pode causar problemas sérios ...

Se você tiver que fazer isso, você pode passar um sinalizador: Aqui está como:

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. Reestruturar código

Deixe sua função aceitar um retorno de chamada. No código de exemplo foo pode ser feito para aceitar um retorno de chamada. Nós estaremos dizendo ao nosso código como reagir quando foo concluído.

Assim:

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

Torna-se:

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

Aqui nós passamos uma função anônima, mas podemos facilmente passar uma referência para uma função existente, fazendo com que pareça:

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

Para obter mais detalhes sobre como esse tipo de design de retorno de chamada é feito, verifique a resposta de Felix.

Agora, vamos definir o próprio foo para agir de acordo

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

(fiddle)

Agora fizemos nossa função foo aceitar uma ação para executar quando o AJAX for concluído com sucesso, podemos estender isso mais, verificando se o status da resposta não é 200 e agindo de acordo (criar um manipulador de falha e tal). Solucionando efetivamente nosso problema.

Se você ainda está tendo dificuldades em entender isso, leia o guia de introdução do AJAX no MDN.

function arrow es6

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



Se você está usando promessas, esta resposta é para você.

Isso significa AngularJS, jQuery (com diferido), substituição de XHR nativo (busca), EmberJS, salvamento do BackboneJS ou qualquer biblioteca de nós que retorne promessas.

Seu código deve ser algo ao longo das linhas deste:

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 fez um ótimo trabalho escrevendo uma resposta para pessoas que usam jQuery com callbacks para AJAX. Eu tenho uma resposta para o XHR nativo. Essa resposta é para uso genérico de promessas no frontend ou no backend.

A questão central

O modelo de simultaneidade do JavaScript no navegador e no servidor com o NodeJS / io.js é assíncrono e reativo .

Sempre que você chama um método que retorna uma promessa, os manipuladores são sempre executados de forma assíncrona - ou seja, após o código abaixo deles que não está em um manipulador .then .

Isso significa que, quando você retornar data o manipulador que você definiu ainda não foi executado. Por sua vez, isso significa que o valor que você está retornando não foi definido para o valor correto no tempo.

Aqui está uma analogia simples para o 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

O valor dos data é undefined pois a parte data = 5 ainda não foi executada. Ele provavelmente será executado em um segundo, mas nesse momento é irrelevante para o valor retornado.

Como a operação ainda não aconteceu (AJAX, chamada do servidor, IO, timer), você está retornando o valor antes que a solicitação tenha a chance de informar ao seu código qual é esse valor.

Uma solução possível para esse problema é codificar reativamente , informando ao seu programa o que fazer quando o cálculo for concluído. As promessas ativamente permitem isso por serem temporais (sensíveis ao tempo) na natureza.

Recapitulação rápida de promessas

Uma promessa é um valor ao longo do tempo . As promessas têm estado, começam como pendentes sem valor e podem se contentar com:

  • preenchido significando que o cálculo foi concluído com sucesso.
  • rejeitado significando que o cálculo falhou.

Uma promessa só pode mudar de estado uma vez depois da qual ela sempre permanecerá no mesmo estado para sempre. Você pode anexar thenmanipuladores a promessas de extrair seu valor e lidar com erros. thenmanipuladores permitem o chaining de chamadas. As promessas são criadas usando APIs que as retornam . Por exemplo, a substituição mais moderna do AJAX fetchou o $.getretorno do jQuery promete.

Quando invocamos .thenuma promessa e retornamos algo dela, obtemos uma promessa para o valor processado . Se retornarmos outra promessa, conseguiremos coisas incríveis, mas vamos segurar nossos cavalos.

Com promessas

Vamos ver como podemos resolver a questão acima com promessas. Primeiro, vamos demonstrar nossa compreensão dos estados de promessa acima usando o construtor Promise para criar uma função de atraso:

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

Agora, depois de convertermos o setTimeout para usar promessas, podemos usá then-lo para fazer valer:

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

Basicamente, em vez de retornar um valor que não podemos fazer por causa do modelo de concorrência - estamos retornando um invólucro para um valor que podemos desembrulhar com then. É como uma caixa com a qual você pode abrir then.

Aplicando isso

Isso é o mesmo para sua chamada API original, você pode:

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

Então, isso funciona tão bem. Aprendemos que não podemos retornar valores de chamadas já assíncronas, mas podemos usar promessas e encadear as mesmas para executar o processamento. Agora sabemos como retornar a resposta de uma chamada assíncrona.

ES2015 (ES6)

ES6 introduz generators que são funções que podem retornar no meio e depois retomar o ponto em que estavam. Isso geralmente é útil para sequências, por exemplo:

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

É uma função que retorna um iterador sobre a seqüência 1,2,3,3,3,3,....que pode ser iterada. Enquanto isso é interessante por si só e abre espaço para muita possibilidade, há um caso interessante em particular.

Se a sequência que estamos produzindo é uma seqüência de ações em vez de números - podemos pausar a função sempre que uma ação é gerada e esperar por ela antes de retomarmos a função. Então, ao invés de uma sequência de números, precisamos de uma seqüência de valores futuros - isto é: promessas.

Esse truque um tanto complicado, mas muito poderoso nos permite escrever um código assíncrono de maneira síncrona. Existem vários "corredores" que fazem isso para você, escrever um é um pequeno número de linhas de código, mas está além do escopo desta resposta. Eu vou estar usando o Bluebird Promise.coroutineaqui, mas existem outros wrappers como coou 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
});

Esse método retorna uma promessa em si, que podemos consumir de outras corrotinas. Por exemplo:

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)

No ES7, isso é padronizado, existem várias propostas agora, mas em todas elas você pode awaitprometer. Isso é apenas "sugar" (melhor sintaxe) para a proposta ES6 acima, adicionando as palavras async- awaitchave e . Fazendo o exemplo acima:

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
}

Ainda retorna uma promessa da mesma forma :)




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



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/




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.




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



Resposta de 2017: agora você pode fazer exatamente o que deseja em todos os navegadores e nós atuais

Isso é bem simples:

  • Devolva uma promessa
  • Use o await , que irá dizer ao JavaScript para aguardar a promessa de ser resolvido em um valor (como a resposta HTTP)
  • Adicione a palavra-chave async/await à função pai

Aqui está uma versão funcional do seu código:

(async function(){

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

})()

Aguardar é suportado em todos os navegadores atuais e nó 8




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




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



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



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.




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



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.






Related