Il est juste de dire que les promesses ne sont que du sucre syntaxique. Tout ce que vous pouvez faire avec des promesses, vous pouvez le faire avec des rappels. En fait, les implémentations les plus prometteuses vous permettent de convertir les deux à tout moment.
La principale raison pour laquelle les promesses sont souvent meilleures est qu'elles sont plus composables , ce qui signifie approximativement que combiner plusieurs promesses "ne fonctionne pas", alors que combiner plusieurs rappels ne le fait souvent pas. Par exemple, il est simple d'affecter une promesse à une variable et d'y attacher des gestionnaires supplémentaires ultérieurement, ou même d'attacher un gestionnaire à un grand groupe de promesses qui ne sera exécutée qu'une fois toutes les promesses résolues. Bien que vous puissiez en quelque sorte émuler ces rappels, il faut beaucoup plus de code, il est très difficile de le faire correctement et le résultat final est généralement beaucoup moins gérable.
L'un des moyens les plus importants (et les plus subtils) d'obtenir une composition est la gestion uniforme des valeurs de retour et les exceptions non détectées. Avec les rappels, la manière dont une exception est gérée peut dépendre entièrement du choix de nombreux rappels imbriqués, et de la fonction qui prend des rappels dans sa mise en œuvre. Avec des promesses, vous savez qu’une exception qui échappe à une fonction de rappel sera interceptée et transmise au gestionnaire d’erreur que vous avez fourni avec .error()
ou .catch()
.
Pour l'exemple que vous avez donné d'un simple rappel par rapport à une seule promesse, il est vrai qu'il n'y a pas de différence significative. C'est quand vous avez un million de rappels contre un million de promesses que le code basé sur les promesses a tendance à être beaucoup plus agréable.
Voici une tentative de code hypothétique écrit avec des promesses, suivi de rappels qui devraient être suffisamment complexes pour vous donner une idée de ce dont je parle.
Avec des promesses:
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);
}
Avec rappels:
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);
}
});
});
});
}
Même sans promesse, il existe peut-être des solutions astucieuses pour réduire la duplication de code dans la version des callbacks, mais toutes celles auxquelles je peux penser se résument à mettre en œuvre quelque chose de très prometteur.