Понимание JavaScript обещаний
Обещание(promise) представляет собой конечный результат асинхронной операции. Это наполнитель, в котором находится значение успешного результата или нематериализуемой причины отказа.
Зачем использовать обещания?
Обещания предоставляют простую альтернативу для выполнения, составления и управления
асинхронными операциями по сравнению с традиционными подходами на основе обратного вызова.
Они также позволяют вам обрабатывать асинхронно ошибки, используя подходы, которые похожи
на синхронный try/catch
.
Состояния обещания
Обещание может быть в одном из 3х состояний:
- Pending - результат обещания пока не определен, потому что асинхронная операция ещё не завершила свою работу
- Fulfilled - асинхронная операция завершилась, обещание имеет значение
- Rejected - асинхронная операция завершилась с ошибкой и обещание никогда не сбудется. В этом состоянии обещание имеет reason, который указывает, из-за чего произошла ошибка
Когда обещание в состоянии pending, оно может мерейти в состояние fulfilled или rejected. После того, как обещание перейдет в состояние fulfilled или rejected, оно никогда больше не перейдет к другому состоянию и её значение или причина отказа не изменится.
Обещания реализованы во многих языках, но пока что это API отличается от языка к языку, JavaScript обещания сведены к предложенному стандарту Promises/A+. EcmaScript 6 запланировал на предоставление обещаний как первостепенную возможность языка и они будут основаны на Promises/A+.
Использование обещаний
Основным API для обещания является его метод then
, который регистрирует обратные вызовы
для получения либо окончательного решения, либо причины, по которой обещание не может быть выполнено.
Здесь представлена простая "hello world" программа, которая асинхронно получает и записывает приветствие.
var greeting = sayHello();
console.log(greeting); // 'hello world’
Кроме того, если sayHello
асинхронный и необходимо просмотреть текущее приветствие
из web-сервиса, он может вернуть обещание:
var greetingPromise = sayHello();
greetingPromise.then(function (greeting) {
console.log(greeting); // 'hello world’
});
Такое же сообщение будет напечатано в консоль, но теперь другой код может продолжить работу.
Как было упомянуто выше, обещание может также представлять сбой. Если сеть упала и приветствие
не может быть доставлено от web-сервиса, вы можете зарегистрировать обработчиксбоя, используя
второй аргумент метода then
:
var greetingPromise = sayHello();
greetingPromise.then(function (greeting) {
console.log(greeting); // 'hello world’
}, function (error) {
console.error('uh oh: ', error); // 'uh oh: something bad happened’
});
Если sayHello
отработало успешно, приветствие будет отображено, но если не успешно,
то тогда причина, т.е. ошибка будет отображена с использованием console.error
.
Трансформация будущих значений
Одной из сильных сторон обещаний является трансформация будущих значений, возвращая новое
значение из функции обратного вызова, передаваемой в then
. К примеру:
var greetingPromise = sayHello();
greetingPromise.then(function (greeting) {
return greeting + '!!!!';
}).then(function (greeting) {
console.log(greeting); // 'hello world!!!!’
});
Последовательность асинхронных операций
Функция, передаваемая в then
может также возвращать другое обещание. Это позволяет
объединять в цепочку асинхронные операции, так что они гарантированно произойдут в том порядке,
в каком они указаны. К примеру, если addExclamation
асинхронный(допустим, что необходим
доступ к другому web-сервису) и возвращает обещание для нового приветствия:
var greetingPromise = sayHello();
greetingPromise.then(function (greeting) {
return addExclamation(greeting); // addExclamation returns a promise
}).then(function (greeting) {
console.log(greeting); // 'hello world!!!!’
});
Это может быть записано более просто:
var greetingPromise = sayHello();
greetingPromise
.then(addExclamation)
.then(function (greeting) {
console.log(greeting); // 'hello world!!!!’
});
Обработка ошибок
Что делать, если произошла ошибка, пока выполнялась асинхронная операция? К примеру,
если sayHello
или addExclamation
завершатся неудачно? В
синхронном коде вы можете использовать try/catch
и быть уверенным в
обработке ошибок в одном месте. Ниже представлена версия синхронного кода предыдущего
примера, который содержит try/catch
обработку ошибки. Если ошибка произошла
в sayHello
или addExclamation
, то будет выполнен блок catch
:
var greeting;
try {
greeting = sayHello();
greeting = addExclamation(greeting);
console.log(greeting); // 'hello world!!!!’
} catch(error) {
console.error('uh oh: ', error); // 'uh oh: something bad happened’
}
Когда речь идет об асинхронных операциях, try/catch
больше не может быть использован.
Несмотря на это, обещания позволяют обрабатывать асинхронные ошибки очень похожим способом. Это
позволяет вам не только писать асинхронный код, похожий на синхронный, но и позаботиться об
асинхронном потоке управления и обработке ошибки подобно синхронному коду.
Ниже приведена версия асинхронного кода, которая обрабатывает ошибки тем же способом:
var greetingPromise = sayHello();
greetingPromise
.then(addExclamation)
.then(function (greeting) {
console.log(greeting); // 'hello world!!!!’
}, function(error) {
console.error('uh oh: ', error); // 'uh oh: something bad happened’
});
Обратите внимание на такой простой синхронный пример, вы можете использовать единственный блок
обработки ошибок, в данном случае как второй параметр последнего вызова then
.
С оригинальным текстом урока вы можете ознакомиться на spring.io.
comments powered by Disqus