可以说,承诺只是语法糖。您可以用诺言做的所有事情都可以用回调做。实际上,大多数promise实现都提供了在您需要时在两者之间进行转换的方法。
承诺通常更好的一个深层原因是它们更具可组合性,这大致意味着将多个promise组合起来“ 行之有效 ”,而将多个回调组合在一起通常不行。例如,将一个promise分配给一个变量并在以后附加附加的处理程序,或者甚至将一个处理程序附加到一大批promise上,只有在所有promise解析之后才执行,这很简单。虽然你可以排序的效仿这些东西回调,它需要更多的代码,是非常难以正确地做,而最终的结果通常是远远低于维护。
诺言获得可组合性的最大(也是最微妙)方法之一是对返回值和未捕获的异常的统一处理。对于回调,如何处理异常可能完全取决于将其嵌套的许多回调中的哪一个,以及采用回调的哪些函数在其实现中具有try / catch。使用promise,您知道将捕获一个逃避一个回调函数的异常并将其传递给您使用.error()
或提供的错误处理程序.catch()
。
对于您给出的单个回调与单个promise的示例,确实没有显着差异。当您有不计其数的回调与不计其数的promise时,基于promise的代码往往看起来更好。
这是尝试用promise编写的一些假设代码,然后是回调,该回调应该足够复杂,以使您了解我在说什么。
有诺言:
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);
}
带有回调:
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);
}
});
});
});
}
即使没有承诺,也可能有一些巧妙的方法可以减少回调版本中的代码重复,但是我能想到的所有方法都归结为实现非常像承诺的东西。