Перевод предложения Марка Беннета на рассмотрение типа Observable в стандартную библиотеку ECMAScript. Актуален по состоянию на 31.03.2017.
Предложение вводит тип Observable в стандартную библиотеку ECMAScript. Тип Observable может использоваться для обработки источников данных, основанных на внешнем воздействии (push), таких как события DOM, интервалы таймера и сокеты. Кроме того, observables являются:
- Композициями: Observables могут быть объединены с комбинаторами высшего порядка.
- Ленивыми: Observables не начинают возвращать данные до тех пор, пока
observer
не подпишется.
Используя конструктор Observable, мы можем создать функцию, принимающую элемент DOM и тип события и возвращающую observable
поток событий.
function listen(element, eventName) {
return new Observable(observer => {
// Создаем обработчик событий, отправляющий данные подписчику
let handler = event => observer.next(event);
// Добавляем подписку на событие
element.addEventListener(eventName, handler, true);
// Возвращаем функцию очистки, которая будет отменять поток событий
return () => {
// Удаляем подписку с элемента
element.removeEventListener(eventName, handler, true);
};
});
}
Затем мы можем использовать стандартные комбинаторы для фильтрации и направления событий в потоке, словно мы используем массив.
// Возвращает observable определенных команд клавиш клавиатуры
function commandKeys(element) {
let keyCommands = { "38": "up", "40": "down" };
return listen(element, "keydown")
.filter(event => event.keyCode in keyCommands)
.map(event => keyCommands[event.keyCode])
}
Примечание: Методы «filter» и «map» не включены в текущее предложение. Они могут быть добавлены в будущую версию этой спецификации.
Когда мы хотим получать поток событий, мы подписываемся на observer
.
let subscription = commandKeys(inputElement).subscribe({
next(val) { console.log("Received key command: " + val) },
error(err) { console.log("Received an error: " + err) },
complete() { console.log("Stream complete") },
});
Объект, возвращаемый subscribe
, позволит нам в любой момент отменить подписку. После отмены, функция очистки Observable будет выполнена.
// После вызова этой функции события больше отправляться не будут
subscription.unsubscribe();
Тип Observable - один из основных протоколов для работы с асинхронными потоками данных. Он особенно эффективен при обработке потоков данных, порождаемых во внешней среде и попадающих в приложение (например, событий пользовательского интерфейса). Предлагая Observable в качестве компонента стандартной библиотеки ECMAScript, мы даем возможность платформам и приложениям совместно использовать основанный на внешнем воздействии общий потоковый протокол.
Для запуска модульных тестов установите пакет es-observable-tests в ваш проект.
npm install es-observable-tests
Затем вызовите экспортированную функцию runTests
с конструктором, который вы хотите протестировать.
require("es-observable-tests").runTests(MyObservable);
Observable - последовательность значений, которые можно наблюдать.
interface Observable {
constructor(subscriber : SubscriberFunction);
// Подписываемся на последовательности с observer
subscribe(observer : Observer) : Subscription;
// Подписываемся на последовательности с функциями обратного вызова
subscribe(onNext : Function,
onError? : Function,
onComplete? : Function) : Subscription;
// Возвращаем себя
[Symbol.observable]() : Observable;
// Конвертируем в Observable
static of(...items) : Observable;
// Конвертируем observable или итерируемый в Observable
static from(observable) : Observable;
}
interface Subscription {
// Отменяем подписку
unsubscribe() : void;
// Булевое значение, указывающее на закрытие подписки
get closed() : Boolean;
}
function SubscriberFunction(observer: SubscriptionObserver) : (void => void)|Subscription;
Observable.of
создает Observable из значений, переданных в качестве аргументов. Значения доставляются синхронно при вызове subscribe
.
Observable.of("red", "green", "blue").subscribe({
next(color) {
console.log(color);
}
});
/*
> "red"
> "green"
> "blue"
*/
Observable.from
преобразует свой аргумент в Observable.
- Если аргумент имеет метод
Symbol.observable
, он возвращает результат вызова этого метода. Если результирующий объект не является экземпляром Observable, то он обертывается в Observable, который будет делегировать подписку. - В противном случае предполагается, что аргумент является итерируемым, а значения итерации доставляются синхронно при вызове
subscribe
.
Преобразование из объекта, поддерживающего Symbol.observable
, в Observable:
Observable.from({
[Symbol.observable]() {
return new Observable(observer => {
setTimeout(() => {
observer.next("hello");
observer.next("world");
observer.complete();
}, 2000);
});
}
}).subscribe({
next(value) {
console.log(value);
}
});
/*
> "hello"
> "world"
*/
let observable = new Observable(observer => {});
Observable.from(observable) === observable; // true
Конвертация из итерируемого в Observable:
Observable.from(["mercury", "venus", "earth"]).subscribe({
next(value) {
console.log(value);
}
});
/*
> "mercury"
> "venus"
> "earth"
*/
Observer используется для получения данных от Observable и передаётся как аргумент в subscribe
. Все методы являются необязательными.
interface Observer {
// Получает объект подписки при вызове `subscribe`
start(subscription : Subscription);
// Получает следующее значение в последовательности
next(value);
// Получает ошибку последовательности
error(errorValue);
// Получает уведомление о завершён.и
complete();
}
SubscriptionObserver - нормализованный Observer, который обертывает объект observer
, переданный в subscribe
.
interface SubscriptionObserver {
// Посылает следующее значение в последовательности
next(value);
// Посылает ошибку последовательности
error(errorValue);
// Посылает уведомление о завершён.и
complete();
// Булевое значение, указывающее на закрытие подписки
get closed() : Boolean;
}
Читайте нас на Медиуме, контрибьютьте на Гитхабе, общайтесь в группе Телеграма, следите в Твиттере и канале Телеграма, скоро подъедет подкаст. Не теряйтесь.