Да, обещания - это асинхронные обратные вызовы. Они не могут делать то, что не могут сделать обратные вызовы, и вы сталкиваетесь с теми же проблемами с асинхронностью, что и с обычными обратными вызовами.
Однако обещания - это нечто большее, чем просто обратные вызовы. Это очень мощная абстракция, позволяющая получить более чистый и лучший функциональный код с менее подверженным ошибкам шаблоном.
Так в чем же основная идея?
Обещания - это объекты, представляющие результат одного (асинхронного) вычисления. Они разрешаются к этому результату только один раз. Это означает следующее:
Обещания реализуют шаблон наблюдателя:
- Вам не нужно знать обратные вызовы, которые будут использовать значение до завершения задачи.
- Вместо того, чтобы ожидать обратных вызовов в качестве аргументов ваших функций, вы можете легко
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
создает обещание для комбинированного результата нескольких обещаний параллельного ожидания.
И последнее, но не менее важное, Promises содержат встроенную обработку ошибок. Результатом вычисления может быть то, что либо обещание выполнено со значением, либо оно отклонено по какой-либо причине. Все функции композиции обрабатывают это автоматически и распространяют ошибки в цепочках обещаний, так что вам не нужно беспокоиться об этом явно везде - в отличие от реализации простого обратного вызова. В конце концов, вы можете добавить специальный обратный вызов для всех возникших исключений.
Не говоря уже о необходимости превращать вещи в обещания.
На самом деле это довольно тривиально с хорошими библиотеками обещаний, см. Как преобразовать существующий API обратного вызова в обещания?
person
Bergi
schedule
21.03.2014