Es ist fair zu sagen, dass Versprechen nur syntaktischer Zucker sind. Alles, was Sie mit Versprechungen tun können, können Sie mit Rückrufen tun. Tatsächlich bieten die meisten vielversprechenden Implementierungen Möglichkeiten zur Konvertierung zwischen den beiden, wann immer Sie möchten.
Der tiefe Grund, warum Versprechen oft besser sind, ist, dass sie komponierbarer sind , was ungefähr bedeutet, dass das Kombinieren mehrerer Versprechen "einfach funktioniert", während das Kombinieren mehrerer Rückrufe oft nicht funktioniert. Zum Beispiel ist es trivial, einer Variablen ein Versprechen zuzuweisen und später zusätzliche Handler hinzuzufügen, oder sogar einen Handler an eine große Gruppe von Versprechungen anzuhängen, die erst ausgeführt werden, nachdem alle Versprechungen aufgelöst wurden. Während Sie diese Dinge mit Rückrufen emulieren können, ist viel mehr Code erforderlich , es ist sehr schwierig, sie korrekt auszuführen , und das Endergebnis ist in der Regel weitaus weniger wartbar.
Eine der größten (und subtilsten) Möglichkeiten, wie Versprechungen komponiert werden können, ist die einheitliche Behandlung von Rückgabewerten und nicht erfassten Ausnahmen. Wie eine Ausnahme bei Rückrufen behandelt wird, hängt möglicherweise ganz davon ab, welche der vielen verschachtelten Rückrufe sie ausgelöst hat und welche der Funktionen, die Rückrufe entgegennehmen, einen Try / Catch in ihrer Implementierung aufweist. Mit Versprechungen wissen Sie , dass eine Ausnahme, die einer Rückruffunktion entgeht, abgefangen und an den Fehlerhandler übergeben wird, den Sie mit .error()
oder bereitgestellt haben .catch()
.
Für das Beispiel, das Sie für einen einzelnen Rückruf gegenüber einem einzelnen Versprechen gegeben haben, gibt es zwar keinen signifikanten Unterschied. Es ist, wenn Sie eine Unmenge von Rückrufen im Vergleich zu einer Unmenge von Versprechungen haben, dass der Code, der auf Versprechungen basiert, tendenziell viel besser aussieht.
Hier ist ein Versuch mit einem hypothetischen Code, der mit Versprechungen und Rückrufen geschrieben wurde. Er sollte nur so komplex sein, dass Sie eine Vorstellung davon haben, wovon ich spreche.
Mit Versprechen:
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);
}
Mit Rückrufen:
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);
}
});
});
});
}
Es gibt vielleicht einige clevere Möglichkeiten, die Code-Duplizierung in der Callback-Version zu reduzieren, auch ohne Versprechen, aber alle, die ich mir vorstellen kann, laufen darauf hinaus, etwas sehr vielversprechendes zu implementieren.