Данная статья - продолжение статьи Перевод статьи "JavaScript Promises. There and back again." Часть 1. Вступление. В ней речь пойдет о терминологии принятой для описания состояний JavaScript-промисов, о том, в каких браузерах они поддерживаются, и как обеспечить их поддержку в других браузерах. Так же, в кратце, будут рассмотрены js-библиотеки, обеспечивающие сходную функциональность и степень их совместимости с нативным промисами JavaScript.
В этом фрагменте, рассмотрен простейший пример промиса, который, однако, дает понять, как описываются JavaScript-промисы и каким образом они могут ложиться в дизайн.
Перевод второй части - под катом.

Терминология промисов


Доменик Деникола вычитал первый черновик этой статьи и поставил мне двойку ("F") за терминологию. Он посадил меня под домашний арест, заставил 100 раз переписать Состояния и Исходы(States and Fates), и напсал обеспокоенное письмо моим родителям. Несмотря на это, у меня все еще некоторая путаница с терминологией, но вот основная:

Промис может быть:
fulfilled — Действие относящееся к промису завершилось успешно
rejected — Действие относящееся к промису завершилось неудачей
pending — Промис еще не перешел в состояние fulfilled или rejected
settled — Промис перешел в состояние fulfilled или rejected
Спецификация так же использует термин thenable, для описания объектов, промисоподобных в том смысле, что они имеют метод then. Этот термин напоминает мне бывшего английского футбольного менеджера Терри Венейблса(Terry Venables), так что я буду стараться пореже его употреблять.

Промисы появляются в JavaScript!


Промисы долгое время существовали в форме библиотек, таких как:
Q
when
WinJS
RSVP.js
Все вышеприведенные и промисы в JavaScript объединяет стандартизированное поведение, которое принято называть Promises/A+. Если Вы пользователь jQuery user, то в нем, есть нечто подобное, под названием Deferred. Однако, Deferred неподходит под классификацию Promise/A+, что несмотря на их сходство, означает, что они все же отличаются, так что будьте бдительны. В jQuery так же есть тип Promise, но он просто подтип Deferred и с ним те же проблемы.

Хотя реализация промисов и стандартизирована, их API отличаются. Промисы JavaScript имеют API сходный с RSVP.js. Вот пример создания промиса:
var promise = new Promise(function(resolve, reject) {
// делаем что-то, возможно асинхронное, затем…

if (/* если все завешилось успешно*/) {
resolve("Сработало!");
}
else {
reject(new Error("Сломалось:("));
}
});

Конструктор промиса принмает один аргумент, это функция от двух параметров resolve и reject. Внутри нее делается что-то, возможно асинхронное, затем, вызывается resolve, если все закончилось хорошо, и reject, в противном случаи.

Как и "throw" в старом добром JavaScript, зачастую, хоть и не обязательно, отказ происходит с объектом типа Error. Преимущество Error-объектов в том, что они позволяют сохранить стек вызовов, что значительно упрощает отладку.
Вот применение этого промиса:
promise.then(function(result) {
console.log(result); // "Сработало!"
}, function(err) {
console.log(err); // Error: "Сломалось:("
});

мето "then" принимает два аргумента, функцию, вызываемую при успешном завершении и функцию, вызваемую при неудачном завершении. Оба параметра необязательные, так что можно указать, и только функцию успеха, и только функцию неудачи.

Промисы JavaScript изначально находились в DOM под названием "Futures", за тем были переименнованы в "Promises", и в итоге перенесены в JavaScript. То, что они находятся в JavaScript, а не в DOM отлично, поскольку это позволяет использовать их в не-браузерном JS-контексте, например, в Node.js (будут ли они пристутствовать в API их ядра уже другой вопрос).

Хотя они и особенность JavaScript, DOM так же использует их. В частности, все новые DOM API с асинхронными "успех/неудача"-методами будут использовать промисы. Это уже происходит с событиями загрузки штифтов, потоками, управлением квотой, ServiceWorker, Web MIDI, и прочими.

Поддержка в брузерах и polyfill


В современных браузерах уже есть поддержка промисов.
Начиная с Chrome 32 и Opera 19, промисы в этих браузерах поддерживаются по умолчанию. Если Вы убежденный пользователь Firefox, возьмите их последний найтли билд, в нем есть частичная поддержка промисов.
Для того, чтоб привести браузер с неполной поддержкой промисов(или вообще без таковой) к соответствию спецификации, либо добавить их поддержку в Node.js, используйте polyfill (2k gzipped).

Совместимость с другими библиотеками


Промис API в JavaScript будет работать с любым объектом имеющим метод then, как с промисообразным объектом(или "thenable"), и, исключая Promise.cast, не делает никакого различия между JavaScript промисами и промисообразным объектами. Так что, если Вы используете библиотеку, которая возвращает Q-промис, то нет никаких проблем, поскольку она будет отлично работать с новый API JavaScript промисов.
Однако, как я уже отмечал ранее, Deferred в jQuery несколько… бесполезны. К счастью, Вы можете их привести к стандартным промисам, что следует сделать как можно скорее:
var jsPromise = Promise.cast($.ajax('/whatever.json'));

Тут, метод jQuery $.ajax возвращает Deferred. Поскольку у него есть метод "then", Promise.cast может превратить его в промис JavaScript. Однако, иногда deferred'ы передают в свои функции множественные аргументы:
var jqDeferred = $.ajax('/whatever.json');

jqDeferred.then(function(response, statusText, xhrObj) {
// ...
}, function(xhrObj, textStatus, err) {
// ...
});

В то время, как промисы JS игнорируют все аргументы, кроме первого:
jsPromise.then(function(response) {
// ...
}, function(xhrObj) {
// ...
});

…к счастью, это обычно то, что Вам нужно, или, по крайней мере, дает возможность получить доступ к тому что нужно. Так же, обратите внимание на то, что jQuery не следует конвенции, и не передает Error-объекты в функции отказа.
0

Комментарии

Для того, чтоб оставлять комментарии или зарегистрируйтесь.