O que são retornos de chamada adiados?

15

Entendo a idéia de um retorno de chamada, onde passo uma função para outra função e essa função usa a função fornecida à vontade.

Estou lutando para entender retornos de chamada adiados, mesmo depois de pesquisar no Google.

Alguém poderia fornecer uma explicação simples, por favor? Eu programo em Ruby, mas também sei um pouco de C / C ++, mas acima de tudo, eu era um programador de linguagem assembly experiente. Então, eu estou querendo saber, é um pouco como uma pilha de endereços de retorno de chamada que são popped? Espero aprender jquery ou node.js e esses retornos de chamada adiados parecem essenciais para ambos. Entendo os princípios básicos de segmentação (embora o objeto mutex faça minha cabeça doer;)

dez vezes
fonte
Você quer dizer Deferredobjetos do jQuery ? Isso é algo específico do Node.js?
precisa saber é o seguinte
1
Não, eu quero dizer em geral. Embora eu queira aprender jquery e possivelmente node.js, senti que precisava entender o que é realmente um primeiro retorno de chamada adiado. Li o artigo da Wikipedia sobre retornos de chamada, mas não consegui entender os retornos adiados, que parecem intrínsecos ao paradigma da operação assíncrona que estará envolvida nesses idiomas que o utilizam.
1
Estou realmente pedindo a idéia conceitual de retorno de chamada diferido em oposição à sua implementação - desculpe se eu não deixei isso mais claro. Dei mais exemplos de linguagem para explicar a idéia que estou tentando esclarecer e também meu histórico de programação para que as pessoas saibam como dar a resposta. Muito obrigado pelas respostas até agora - estou chegando lá!
tentimes
Ok, acho que já entendi, pessoal, obrigado a todos! Eu não sei como responder a isso. Cameron explicou o conceito de maneira mais simples e foi isso que eu realmente estava procurando, mas outros também entraram em cena e acrescentaram ao meu conhecimento. Não estou certo de que maneira a aceitar a resposta como eu sou novo para isso;)
tentimes

Respostas:

4

Por solicitação, aqui estão os comentários apresentados como resposta:


Não sei se você entendeu completamente o fato de que funções em JS são objetos de primeira classe e, portanto, podem ser armazenadas até serem necessárias, após o tempo em que são criadas.

Por exemplo, suponha que você queira gravar em um arquivo e imprima uma mensagem de log; então você chama a função "write ()" (ou o que for) e passa a ela uma função que gera a mensagem de log (essa é a função de retorno de chamada adiada). "write ()" armazena internamente uma referência à função especificada, começa a gravar no arquivo e configura seu próprio retorno de chamada para saber quando a gravação é concluída. Em seguida, ele retorna antes que a gravação seja concluída; quando é, o retorno de chamada interno é chamado de alguma forma (esse é o trabalho da estrutura subjacente - no caso do node.js, é feito com um loop de eventos), que chama o retorno de chamada que imprime a mensagem de log.

A parte "adiada" significa simplesmente que sua função de retorno de chamada não é chamada imediatamente; chamando isso é adiado até o momento apropriado. No caso de funções assíncronas como muitas no node.js, o retorno de chamada fornecido geralmente é chamado quando a operação é concluída (ou ocorre um erro).

A maioria das coisas é assíncrona no node.js, mas no navegador com, por exemplo, jQuery, a maioria das coisas é realmente síncrona (exceto, obviamente, para solicitações AJAX). Como as funções de primeira classe são muito úteis em JavaScript (especialmente por causa do excelente suporte de fechamento), os retornos de chamada também são usados ​​em todos os lugares do navegador, mas não são "adiados" para operações síncronas (exceto na medida em que não são chamados imediatamente por você, mas mais tarde pela função que você chama).

O fato de o sistema subjacente ser orientado a eventos é ortogonal ao uso de retornos de chamada adiados; você pode imaginar uma versão (muito lenta) do node.js que iniciou um encadeamento para cada operação e, em seguida, chamou seu retorno de chamada quando o encadeamento terminou seu trabalho, sem usar eventos. Claro, este é um modelo horrível, mas ilustra meu ponto :-)

Cameron
fonte
8

A maneira como um retorno de chamada diferido funciona é cada vez que você adiciona um retorno de chamada a ele, esse retorno de chamada é enviado para uma matriz. Em seguida, quando o método .resolve()ou .resolveWith()é chamado no objeto adiado, todos os .done()retornos de chamada na matriz são executados em ordem.

Agora podemos ver o que é um objeto adiado. Veja o trecho abaixo como exemplo.

var deferred = $.Deferred();
var promise = deferred.promise();

O que temos agora é um objeto adiado e o objeto de promessa do objeto adiado. O objeto diferidos tem todos os mesmos métodos que o objeto promessa, no entanto o objeto promessa tem apenas os métodos .done(), .fail()e .always()que são usados para adicionar retornos de chamada para o objeto adiada para cada respectivo event. O objeto adiado, por outro lado, tem vários outros métodos, o mais importante .resolve()e .reject(). Quando esses métodos são chamados no objeto adiado, todos os retornos de chamada são chamados. .resolve()aciona os retornos de chamada .done()e, .always()enquanto o .reject()método chama .fail()e .always()retornos de chamada.

Geralmente, o objeto adiado é mantido oculto em um escopo privado e o objeto de promessa é retornado da função para que os retornos de chamada possam ser colocados nele. O objeto adiado será resolvido posteriormente, como após a conclusão de uma solicitação ajax ou após o carregamento de uma imagem, após um setTimeout, etc. Também é importante perceber que um objeto adiado pode ser resolvido apenas uma vez. Se já estiver resolvido, seus retornos de chamada serão chamados imediatamente.

Aqui está outro exemplo, um que eu uso:

function loadImage(url) {
    var def = $.Deferred(),
        img = new Image();
    $(img).on("load error",function(e){
        if (e.type === "error") {
            def.reject(url);
        }
        else {
            def.resolve(url);
        }
    });
    img.src = url;
    // return the promise object so that callbacks can
    // be defined on the deferred object.
    return def.promise();
}
loadImage("foobar.jpg").done(function(){
    alert("The image is loaded!");
}).fail(function(){
    alert("The image failed to load!");
}).always(function(){
    alert("This is always called!");
});

Para obter mais informações sobre o $.Deferred()método e objetos adiados do jQuery , visite http://api.jquery.com/category/deferred-object/.

user400654
fonte
Provavelmente, isso será inestimável quando eu entender o conceito de retorno de chamada adiado. Desculpe, mas ainda não entendo o conceito do que é um retorno de chamada adiado. Estou procurando mais a idéia conceitual por trás disso. Mais ou menos na idéia de Mihia. Quando eu conseguir entender isso, talvez eu possa entender js.
tentimes
3

Não tenho certeza, mas acredito que um retorno de chamada diferido se refere a um retorno de chamada assíncrono; portanto, você terá melhor sorte no Google.

A melhor explicação que encontrei foi em http://www.nodebeginner.org

Ei, provavelmenteExpensiveFunction (), faça suas coisas, mas eu, o único thread do Node.js., não vou esperar aqui até você terminar, continuarei a executar as linhas de código abaixo de você, por favor, tome esta callbackFunction () aqui e chame quando terminar de fazer suas coisas caras? Obrigado!"

Neste exemplo, provavelmenteExpensiveFunction é uma função sem bloqueio (ou assíncrona). Isso significa que não é executado imediatamente, mas colocado em um chamado loop de evento. O encadeamento node.js continuará a execução, mas em algum momento, ele decidirá executar algo do loop de eventos. Quando atinge provavelmenteExpensiveFunction, ele a chama e, quando provavelmenteExpensiveFunction termina a execução, chama o retorno de chamada (adiado) passado como um parâmetro para ele.

Como um exemplo de provavelmenteExpensiveFunction, você pode usar fs.readFile

mihai
fonte
Obrigado. Mas como a função dispendiosa fará suas coisas se já tiver retornado e você estiver de volta ao segmento principal de execução? Eu não entendo isso. Você está dizendo que a função persiste de alguma maneira depois que termina?
Editou a resposta, talvez esteja mais claro agora.
Muito útil. Mas ... eu tenho que processar essa matriz de retornos de chamada armazenados? O que quero dizer é o que está processando esta lista. É (por exemplo) que js eleva esses retornos de chamada em segundo plano, sem que você faça nada a respeito, ou é que um evento causa o node.js ou algo para chamar um retorno de chamada específico. Desculpe, eu estou recebendo cerca de 70% do que você está dizendo, mas sou apenas um pouco perdido sobre o resto :)
tentimes
@tentimes - "o que está processando esta lista" o objeto $ .Deferred () está processando a lista. Quando você chama .resolve()ou .reject()no objeto adiado original, a lista de retornos de chamada é chamada.
usar o seguinte comando
2
@tentimes: Pelo que você está dizendo, não tenho certeza se você entende completamente o fato de que as funções em JS são objetos de primeira classe e, portanto, podem ser armazenadas até serem necessárias, após o tempo em que são criadas. Por exemplo, digamos que você queira gravar em um arquivo e imprimir uma mensagem de log; então você chama a função "write" (ou qualquer outra coisa) e passa a ela uma função que gera a mensagem de log. "write ()" armazena internamente uma referência à função especificada, começa a gravar no arquivo e configura seu próprio retorno de chamada para saber quando a gravação é concluída. Em seguida, ele retorna antes que a gravação seja concluída; quando é, sua função é chamada.
Cameron
3

O JavaScript é de thread único, portanto você não pode pensar em termos de threads para entender isso. Aqui está um exemplo de retornos de chamada regulares e assíncronos usando jQuery:

var regularCallback = function(evt) {
    alert("I'm a callback!")
}
var asyncCallback = function(data) {
    alert("I only run when an async operation finishes!")
}

// Bind the regular callback to a button's click event
$('#mybutton').on('click', regularCallback);

// Start an ajax request to the server. The request is asynchronous, so code
// below this line will execute immediately. The callback function
// will only be called when the request is complete.
$.get("http://google.com", asyncCallback);
bfavaretto
fonte
Agora isso está me levando a algum lugar, obrigado. Portanto, o assíncrono é uma resposta a um evento que eu configurei com o ajax - que apenas faz isso e, se o evento acontecer, meu retorno de chamada será chamado? Eu acho que estou entendendo. O node.js / jquery faria algo semelhante ao jquery usando objetos adiados e um sistema complicado de interação com eles para realmente fazer a mesma coisa, mas usando métodos?
tentimes
1
@tentimes, exatamente! Os retornos de chamada geralmente são executados em resposta a eventos (mas como "retorno de chamada" não é uma construção de linguagem js, às vezes o termo é usado em outros contextos). Os objetos adiados (promessas) que você vê na resposta de Kevin são basicamente açúcar sintático. Eles são "resolvidos" ou "rejeitados" quando algum evento (assíncrono) é acionado e, em seguida, chamam o retorno de chamada apropriado ("concluído" ou "falha" e depois "sempre"). O uso deles no jQuery pode tornar o código mais legível e permitir alguns truques adicionais (como adicionar facilmente um segundo retorno de chamada após disparar uma solicitação ajax, por exemplo).
precisa saber é o seguinte
Ambos são chamadas de retorno assíncronas
Raynos
1

Retornos de chamada adiados (também conhecidos como Promoções ) permitem que você escreva código assíncrono seqüencial, sem dor e espaguete de retorno de chamada:

$.when( doAjax(), doAnotherAjax() ).then( haveFunWithMoreAjax ).then( animateStuff );

'when' permite esperar as funções retornarem em paralelo e thenpode ser encadeado sequencialmente.

uma observação: jQuery adiado ! = Promoções / A , sua sintaxe é um pouco diferente.

Existem bons artigos sobre o assunto: um no IEBlog e outro em algum blog aleatório , um livro e uma pergunta popular sobre o stackoverflow

c69
fonte
1
uhh .. ao que parece - OP perguntou algo ligeiramente diferente .. bem, vamos esperar que esta resposta ajude outra pessoa, algum dia, talvez.
C69