Да, Обещания являются асинхронными обратными вызовами. Они не могут делать ничего, что не могут делать обратные вызовы, и вы сталкиваетесь с такими же проблемами с асинхронностью, как и с простыми обратными вызовами.
Тем не менее, обещания это больше, чем просто обратные вызовы. Это очень мощная абстракция, позволяющая создавать более чистый и качественный функциональный код с менее подверженным ошибкам образцом.
Так в чем же основная идея?
Обещания - это объекты, представляющие результат одного (асинхронного) вычисления. Они разрешают этот результат только один раз. Есть несколько вещей, что это значит:
Обещания реализуют шаблон наблюдателя:
- Вам не нужно знать обратные вызовы, которые будут использовать значение до завершения задачи.
- Вместо ожидания обратных вызовов в качестве аргументов ваших функций, вы можете легко
return
получить объект Promise.
- Обещание будет хранить значение, и вы можете прозрачно добавить обратный вызов, когда захотите. Он будет вызван, когда будет доступен результат. «Прозрачность» подразумевает, что когда у вас есть обещание и вы добавляете к нему обратный вызов, для вашего кода не имеет значения, достигнут ли результат - API и контракты одинаковы, что значительно упрощает кэширование / запоминание.
- Вы можете легко добавить несколько обратных вызовов
Обещания являются связными ( монадические , если хотите ):
- Если вам нужно преобразовать значение, которое представляет обещание, вы сопоставляете функцию преобразования с обещанием и получаете новое обещание, которое представляет преобразованный результат. Вы не можете синхронно получить значение, чтобы использовать его как-то, но вы можете легко поднять преобразование в контексте обещания. Нет стандартных обратных вызовов.
- Если вы хотите связать две асинхронные задачи, вы можете использовать
.then()
метод. Он будет принимать обратный вызов, который будет вызван с первым результатом, и возвращает обещание для результата обещания, которое возвращает обратный вызов.
Звучит сложно? Время для примера кода.
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)
Сглаживание не происходит волшебным образом, но вы можете легко это сделать. Для вашего сильно вложенного примера, (почти) эквивалент будет
api1().then(api2).then(api3).then(/* do-work-callback */);
Если просмотр кода этих методов помогает понять, вот основная библиотека обещаний в нескольких строках .
В чем суета обещаний?
Абстракция Promise позволяет намного лучше комбинировать функции. Например, рядом с then
цепочкой all
функция создает обещание для комбинированного результата нескольких обещаний параллельного ожидания.
Последнее, но не менее важное Обещания идут с интегрированной обработкой ошибок. Результатом вычисления может быть то, что либо обещание выполнено со значением, либо оно отклонено по причине. Все функции компоновки обрабатывают это автоматически и распространяют ошибки в цепочках обещаний, так что вам не нужно явно заботиться об этом повсюду - в отличие от реализации простого обратного вызова. В конце концов, вы можете добавить выделенный обратный вызов для всех возникших исключений.
Не говоря уже о необходимости превращать вещи в обещания.
Это довольно тривиально на самом деле с хорошими библиотеками обещаний, см. Как преобразовать существующий API обратного вызова в обещания?