Sim, promessas são retornos de chamada assíncronos. Eles não podem fazer nada que os retornos de chamada não possam fazer, e você enfrenta os mesmos problemas com assincronia e com retornos simples.
No entanto, as promessas são mais do que apenas retornos de chamada. Eles são uma abstração muito poderosa, permitem códigos funcionais mais limpos e melhores com clichê menos propenso a erros.
Então, qual é a ideia principal?
Promessas são objetos que representam o resultado de uma única computação (assíncrona). Eles resolvem esse resultado apenas uma vez. Existem algumas coisas que isso significa:
Promessas implementam um padrão de observador:
- Você não precisa conhecer os retornos de chamada que usarão o valor antes que a tarefa seja concluída.
- Em vez de esperar retornos de chamada como argumentos para suas funções, você pode facilmente
return
um objeto Promise
- A promessa armazenará o valor e você poderá adicionar um retorno de chamada de forma transparente sempre que desejar. Será chamado quando o resultado estiver disponível. "Transparência" implica que, quando você tem uma promessa e adiciona um retorno de chamada, não faz diferença para o seu código se o resultado já chegou - a API e os contratos são os mesmos, simplificando muito o cache / memória.
- Você pode adicionar vários retornos de chamada facilmente
As promessas são encadeadas ( monádicas , se você quiser ):
- Se você precisar transformar o valor que uma promessa representa, você mapeie uma função de transformação sobre a promessa e receba de volta uma nova promessa que represente o resultado transformado. Você não pode obter de forma síncrona o valor para usá-lo de alguma forma, mas pode facilmente elevar a transformação no contexto da promessa. Não há retornos de chamada padrão.
- Se você deseja encadear duas tarefas assíncronas, pode usar o
.then()
método É necessário um retorno de chamada para ser chamado com o primeiro resultado e retorna uma promessa para o resultado da promessa que o retorno de chamada retorna.
Parece complicado? Hora de um exemplo de código.
var p1 = api1(); // returning a promise
var p3 = p1.then(function(api1Result) {
var p2 = api2(); // returning a promise
return p2; // The result of p2 …
}); // … becomes the result of p3
// So it does not make a difference whether you write
api1().then(function(api1Result) {
return api2().then(console.log)
})
// or the flattened version
api1().then(function(api1Result) {
return api2();
}).then(console.log)
O achatamento não ocorre magicamente, mas você pode fazê-lo facilmente. Para o seu exemplo fortemente aninhado, o equivalente (quase) seria
api1().then(api2).then(api3).then(/* do-work-callback */);
Se ver o código desses métodos ajuda a entender, aqui está uma lib de promessa mais básica em algumas linhas .
Qual é o grande alarido sobre promessas?
A abstração Promise permite uma composição de funções muito melhor. Por exemplo, ao lado dethen
de encadeamento, a all
função cria uma promessa para o resultado combinado de várias promessas de espera paralela.
Por último, mas não menos importante, as promessas vêm com o tratamento de erros integrado. O resultado do cálculo pode ser que a promessa seja cumprida com um valor ou seja rejeitada por uma razão. Todas as funções de composição lidam com isso automaticamente e propagam erros em cadeias de promessas, para que você não precise se preocupar com isso explicitamente em todos os lugares - em contraste com uma implementação simples de retorno de chamada. No final, você pode adicionar um retorno de chamada de erro dedicado para todas as exceções ocorridas.
Sem mencionar a necessidade de converter as coisas em promessas.
Isso é bastante trivial, na verdade, com boas bibliotecas de promessas, consulte Como converter uma API de retorno de chamada existente em promessas?