Ao fazer programação assíncrona de thread único, há duas técnicas principais com as quais estou familiarizado. O mais comum é usar retornos de chamada. Isso significa passar para a função que atua de forma assíncrona uma função de retorno de chamada como parâmetro. Quando a operação assíncrona for concluída, o retorno de chamada será chamado.
Algum jQuery
código típico projetado desta maneira:
$.get('userDetails', {'name': 'joe'}, function(data) {
$('#userAge').text(data.age);
});
No entanto, esse tipo de código pode ficar confuso e altamente aninhado quando queremos fazer chamadas assíncronas adicionais uma após a outra quando a anterior termina.
Portanto, uma segunda abordagem está usando o Promises. Uma promessa é um objeto que representa um valor que talvez ainda não exista. Você pode definir retornos de chamada, que serão chamados quando o valor estiver pronto para ser lido.
A diferença entre o Promises e a abordagem de retorno de chamada tradicional é que agora os métodos assíncronos retornam objetos Promise de forma síncrona, na qual o cliente define um retorno de chamada. Por exemplo, código semelhante usando Promises no AngularJS:
$http.get('userDetails', {'name': 'joe'})
.then(function(response) {
$('#userAge').text(response.age);
});
Então, minha pergunta é: existe realmente uma diferença real? A diferença parece ser puramente sintática.
Existe alguma razão mais profunda para usar uma técnica sobre a outra?
Respostas:
É justo dizer que promessas são apenas açúcar sintático. Tudo o que você pode fazer com promessas que você pode fazer com retornos de chamada. De fato, a maioria das implementações promissoras fornece formas de conversão entre as duas quando você quiser.
A razão profunda pela qual as promessas costumam ser melhores é que elas são mais compostáveis , o que significa aproximadamente que combinar várias promessas "simplesmente funciona", enquanto a combinação de vários retornos de chamada geralmente não funciona. Por exemplo, é trivial atribuir uma promessa a uma variável e anexar manipuladores adicionais a ela mais tarde, ou até anexar um manipulador a um grande grupo de promessas que são executadas somente após todas as promessas serem resolvidas. Embora você possa emular essas coisas com retornos de chamada, é preciso muito mais código, é muito difícil de executar corretamente, e o resultado final geralmente é muito menos sustentável.
Uma das maiores (e mais sutis) maneiras pelas quais as promessas ganham sua composição é através do tratamento uniforme dos valores de retorno e das exceções não capturadas. Com os retornos de chamada, a maneira como uma exceção é tratada pode depender inteiramente de qual dos muitos retornos de chamada aninhados a lançou e qual das funções que recebem retornos de chamada tem uma tentativa / captura em sua implementação. Com promessas, você sabe que uma exceção que escapa de uma função de retorno de chamada será capturada e transmitida ao manipulador de erros que você forneceu
.error()
ou.catch()
.Para o exemplo que você deu de um único retorno de chamada versus uma única promessa, é verdade que não há diferença significativa. É quando você tem um zilhão de retornos de chamada versus um zilhão de promessas que o código baseado em promessas tende a parecer muito melhor.
Aqui está uma tentativa de algum código hipotético escrito com promessas e, em seguida, com retornos de chamada que devem ser complexos o suficiente para lhe dar uma idéia do que estou falando.
Com promessas:
Com retornos de chamada:
Pode haver algumas maneiras inteligentes de reduzir a duplicação de código na versão de retorno de chamada, mesmo sem promessas, mas todas as que posso pensar se resumem a implementar algo muito parecido com promessa.
fonte
yield
promessas. A vantagem aqui é que você tem a capacidade de misturar estruturas de fluxo de controle nativo, que podem variar em quantas operações assíncronas são executadas. Vou adicionar uma versão que mostra isso.then(callback)
método no Promise que aceita um retorno de chamada (em vez de um método na API que aceita esse retorno de chamada) não precisa fazer nada com a IoC. O Promise introduz um nível de indireção que é útil para composição, encadeamento e tratamento de erros (programação orientada a transporte ferroviário em vigor), mas o retorno de chamada ainda não é executado pelo cliente, portanto, não há realmente ausência de IoC.