Понимание 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