Es justo decir que las promesas son solo azúcar sintáctica. Todo lo que puede hacer con promesas lo puede hacer con devoluciones de llamada. De hecho, la mayoría de las implementaciones prometedoras proporcionan formas de convertir entre los dos cuando lo desee.
La razón profunda por la cual las promesas son a menudo mejores es que son más componibles , lo que significa que combinar múltiples promesas "simplemente funciona", mientras que combinar múltiples devoluciones de llamadas a menudo no lo hace. Por ejemplo, es trivial asignar una promesa a una variable y adjuntar controladores adicionales más adelante, o incluso adjuntar un controlador a un gran grupo de promesas que se ejecutan solo después de que se resuelven todas las promesas. Si bien puede emular estas cosas con devoluciones de llamada, se necesita mucho más código, es muy difícil de hacer correctamente y el resultado final generalmente es mucho menos fácil de mantener.
Una de las formas más grandes (y sutiles) de que las promesas ganen su componibilidad es mediante el manejo uniforme de los valores de retorno y las excepciones no detectadas. Con las devoluciones de llamada, la forma en que se maneja una excepción puede depender completamente de cuál de las muchas devoluciones de llamada anidadas la arrojó, y cuál de las funciones que toman devoluciones de llamada tiene un intento / captura en su implementación. Con las promesas, sabe que una excepción que escapa a una función de devolución de llamada se detectará y pasará al controlador de errores que proporcionó con .error()
o .catch()
.
Para el ejemplo que usted dio de una única devolución de llamada versus una sola promesa, es cierto que no hay una diferencia significativa. Es cuando tienes un billón de devoluciones de llamada versus un billón de promesas que el código basado en promesas tiende a verse mucho mejor.
Aquí hay un intento de un código hipotético escrito con promesas y luego con devoluciones de llamada que deberían ser lo suficientemente complejas como para darle una idea de lo que estoy hablando.
Con promesas:
createViewFilePage(fileDescriptor) {
getCurrentUser().then(function(user) {
return isUserAuthorizedFor(user.id, VIEW_RESOURCE, fileDescriptor.id);
}).then(function(isAuthorized) {
if(!isAuthorized) {
throw new Error('User not authorized to view this resource.'); // gets handled by the catch() at the end
}
return Promise.all([
loadUserFile(fileDescriptor.id),
getFileDownloadCount(fileDescriptor.id),
getCommentsOnFile(fileDescriptor.id),
]);
}).then(function(fileData) {
var fileContents = fileData[0];
var fileDownloads = fileData[1];
var fileComments = fileData[2];
fileTextAreaWidget.text = fileContents.toString();
commentsTextAreaWidget.text = fileComments.map(function(c) { return c.toString(); }).join('\n');
downloadCounter.value = fileDownloads;
if(fileDownloads > 100 || fileComments.length > 10) {
hotnessIndicator.visible = true;
}
}).catch(showAndLogErrorMessage);
}
Con devoluciones de llamada:
createViewFilePage(fileDescriptor) {
setupWidgets(fileContents, fileDownloads, fileComments) {
fileTextAreaWidget.text = fileContents.toString();
commentsTextAreaWidget.text = fileComments.map(function(c) { return c.toString(); }).join('\n');
downloadCounter.value = fileDownloads;
if(fileDownloads > 100 || fileComments.length > 10) {
hotnessIndicator.visible = true;
}
}
getCurrentUser(function(error, user) {
if(error) { showAndLogErrorMessage(error); return; }
isUserAuthorizedFor(user.id, VIEW_RESOURCE, fileDescriptor.id, function(error, isAuthorized) {
if(error) { showAndLogErrorMessage(error); return; }
if(!isAuthorized) {
throw new Error('User not authorized to view this resource.'); // gets silently ignored, maybe?
}
var fileContents, fileDownloads, fileComments;
loadUserFile(fileDescriptor.id, function(error, result) {
if(error) { showAndLogErrorMessage(error); return; }
fileContents = result;
if(!!fileContents && !!fileDownloads && !!fileComments) {
setupWidgets(fileContents, fileDownloads, fileComments);
}
});
getFileDownloadCount(fileDescriptor.id, function(error, result) {
if(error) { showAndLogErrorMessage(error); return; }
fileDownloads = result;
if(!!fileContents && !!fileDownloads && !!fileComments) {
setupWidgets(fileContents, fileDownloads, fileComments);
}
});
getCommentsOnFile(fileDescriptor.id, function(error, result) {
if(error) { showAndLogErrorMessage(error); return; }
fileComments = result;
if(!!fileContents && !!fileDownloads && !!fileComments) {
setupWidgets(fileContents, fileDownloads, fileComments);
}
});
});
});
}
Puede haber algunas formas inteligentes de reducir la duplicación de código en la versión de devolución de llamada incluso sin promesas, pero todas las que se me ocurren se reducen a implementar algo muy prometedor.